Skip to content

Commit e98bfbb

Browse files
author
Songyu
committed
更新文章
1 parent 9c9406f commit e98bfbb

18 files changed

+535
-61
lines changed

_posts/2020-11-18-springmvc整合spring-security(1)启用配置文件登录验证.md renamed to _posts/2020-11-18-springmvc整合spring-security启用配置文件登录验证.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
22
layout: post
3-
title: "springmvc整合spring security(1)启用配置文件登录验证"
3+
title: "springmvc整合spring security启用配置文件登录验证"
44
date: 2020-11-18 19:52:31 +0800
55
tags:
66
description:
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
---
2+
layout: post
3+
title: "springmvc整合spring security数据库进行验证"
4+
date: 2020-12-04 18:26:43 +0800
5+
tags: java
6+
description:
7+
---
8+
9+
之前介绍了security最快捷的一种登录方式,配置文件验证。将要登陆的用户、角色信息写在配置文件中,进行登录验证。但是在实际项目中,基本不会这么去使用,对于用户的认证信息是要从数据库中进行查找和验证。
10+
11+
这次要记录的就是通过自定义的用户查询逻辑,对用户进行认证
12+
13+
## XML配置
14+
15+
首先要将之前的security:authentication-manager配置进行修改
16+
17+
{% highlight xml %}
18+
<security:authentication-manager id="demo">
19+
<security:authentication-provider user-service-ref="userService">
20+
<!-- 加密方式 -->
21+
<security:password-encoder ref="passwordEncoder"></security:password-encoder>
22+
</security:authentication-provider>
23+
</security:authentication-manager>
24+
<!-- 自定义的用户呀验证类 -->
25+
<bean id="userService" class="com.bc.common.security.authc.CustomerUserDetailService"></bean>
26+
<!-- 自定义密码验证类(security提供了md5的加密,这里是为了运用一下自定义密码验证类的配置) -->
27+
<bean id="passwordEncoder" class="com.bc.common.security.password.MD5PasswordEncoder"></bean>
28+
{% endhighlight %}
29+
30+
同时还需要对之前的http配置中增加登录的页面、请求url等。对于登录页面的编写,就不在说明了,就是正常的页面渲染而已
31+
32+
{% highlight xml %}
33+
<security:http auto-config="true" authentication-manager-ref="demo">
34+
<security:headers>
35+
<!-- iframe保护 -->
36+
<security:frame-options policy="SAMEORIGIN"></security:frame-options>
37+
</security:headers>
38+
<!-- 配置需要登录的url -->
39+
<security:intercept-url pattern="/demo/**" access="hasAnyAuthority('ADMIN')" />
40+
<!-- 配置自定义登录 -->
41+
<!--
42+
login-page 登录页url
43+
login-processing-url 提交登录的访问url。
44+
default-target-url 登录后访问页面
45+
-->
46+
<security:form-login
47+
login-page="/backend/enter/login/index"
48+
login-processing-url="/backend/enter/login/ajaxurl"
49+
default-target-url="/demo/data/index"/>
50+
<!-- csrf配置 -->
51+
<security:csrf disabled="true"></security:csrf>
52+
</security:http>
53+
{% endhighlight %}
54+
55+
## 编写验证类
56+
57+
对于用户验证类,我们要实现security中的UserDetailsService接口中的loadUserByUsername函数。来获取用户的信息
58+
59+
{% highlight java %}
60+
public class CustomerUserDetailService implements UserDetailsService {
61+
62+
@Autowired
63+
private AdminMapper adminMapper;
64+
65+
@Override
66+
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
67+
//自定义用户查询逻辑。通过用户名查询
68+
QueryWrapper<AdminEntity> queryWrapper = new QueryWrapper<>();
69+
queryWrapper.eq("username",username)
70+
.eq("status",1);
71+
AdminEntity adminEntity = adminMapper.selectOne(queryWrapper);
72+
if(adminEntity == null){
73+
throw new UsernameNotFoundException("用户名或密码错误!");
74+
}
75+
//存在请求用户名下的用户信息,赋予默认的权限。创建User对象时权限不能为空
76+
List<GrantedAuthority> a = AuthorityUtils.commaSeparatedStringToAuthorityList("ADMIN");
77+
78+
//这里将用户名,密码,权限传入User中。security会自动把请求密码加密和用户中的密码进行比对
79+
User user = new User(username,adminEntity.getPassword(),a);
80+
return user;
81+
}
82+
}
83+
{% endhighlight %}
84+
85+
密码验证类,实现PasswordEncoder接口中的encode(加密)、matches(比对)函数
86+
87+
{% highlight java %}
88+
public class MD5PasswordEncoder implements PasswordEncoder {
89+
90+
@Override
91+
public String encode(CharSequence charSequence) {
92+
return Md5.encryp((String)charSequence);
93+
}
94+
95+
@Override
96+
public boolean matches(CharSequence charSequence, String s) {
97+
return Md5.verify((String)charSequence,s);
98+
}
99+
}
100+
{% endhighlight %}
101+
102+
上述工作做完之后,再次访问我们的/demo/data/index路径,发现自动跳转到我们自定义的登录页面中
103+
104+
![](/images/2020-12-04-1.jpg)
105+
106+
输入数据库中的用户名和密码,成功进入到页面中
107+
108+
![](/images/2020-12-04-2.jpg)
109+
110+
springmvc + spring security的登录整合就完成了,对比之前我们自己写的登录验证、跳转的逻辑,省去了大把的时间。降低了工作量和难度
111+
112+
![](/images/2020-12-04-3.jpg)
Lines changed: 246 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,246 @@
1+
---
2+
layout: post
3+
title: "spring boot项目练习(一)整合spring security登录"
4+
date: 2020-12-26 17:26:35 +0800
5+
tags: java
6+
description:
7+
---
8+
9+
最近看了好多spring boot的资料,也一直在学习,准备动手,自己写一个接口测试的系统来使用,正好也有这个需求。
10+
11+
自己会尽量的把开发过程中遇到的问题,或者一些东西记录下来,尽量不太监。。哈哈
12+
13+
对于用户登录,本来想使用之前用过的shiro,但是spring boot已经默认配置好了spring security。索性就直接使用security来管理用户
14+
15+
## 引入包
16+
17+
> 只需要简单的引入两个包就可以使用了
18+
19+
{% highlight java %}
20+
<dependency>
21+
<groupId>org.springframework.boot</groupId>
22+
<artifactId>spring-boot-starter-security</artifactId>
23+
</dependency>
24+
<dependency>
25+
<groupId>org.springframework.security</groupId>
26+
<artifactId>spring-security-test</artifactId>
27+
<scope>test</scope>
28+
</dependency>
29+
{% endhighlight %}
30+
31+
## 配置登录
32+
33+
> 对于security默认的登录,不在介绍了,引入包之后,再次访问会进入默认的登录页面,默认的登录名是user,密码会打印在控制台中
34+
> 默认形式,基本不会使用在项目中,我们这里直接介绍自定义登录和响应的用法
35+
36+
### 控制器+页面
37+
38+
> 控制器和页面几乎不需要多余的操作
39+
> 控制器只需要渲染视图
40+
> 页面只需要进行提交就可以了
41+
> 其他的认证操作都交给security去处理就可以了
42+
43+
控制器
44+
45+
{% highlight java %}
46+
@Controller(value = "loginController")
47+
@RequestMapping(value = "login")
48+
public class LoginController extends BaseController {
49+
50+
@RequestMapping(value = "index")
51+
public ModelAndView index(){
52+
return result("login/index");
53+
}
54+
55+
}
56+
{% endhighlight %}
57+
58+
视图,这里是贴上了form表单和提交js事件,资源文件的引入之类的没有写入,防止无用的东西占用篇幅
59+
60+
{% highlight html %}
61+
<!DOCTYPE html>
62+
<html lang="en" xmlns:th="http://www.thymeleaf.org">
63+
<head>
64+
<title>SB Admin 2 - Login</title>
65+
</head>
66+
<body class="bg-gradient-primary">
67+
<form class="user">
68+
<div class="form-group">
69+
<input type="text" class="form-control form-control-user" name="username" placeholder="用户名">
70+
</div>
71+
<div class="form-group">
72+
<input type="password" class="form-control form-control-user" name="password" placeholder="密码">
73+
</div>
74+
<div class="form-group">
75+
<div class="custom-control custom-checkbox small">
76+
<input type="checkbox" class="custom-control-input" id="customCheck">
77+
<label class="custom-control-label" for="customCheck">Remember Me</label>
78+
</div>
79+
</div>
80+
<button type="button" id="submit" class="btn btn-primary btn-user btn-block">
81+
Login
82+
</button>
83+
</form>
84+
</body>
85+
<script>
86+
$(function () {
87+
$("#submit").click(function () {
88+
submitForm("/login/index",function (message) {
89+
showSuccess(message);
90+
},function (message) {
91+
showWarning(message);
92+
});
93+
});
94+
})
95+
</script>
96+
</html>
97+
{% endhighlight %}
98+
99+
### security自定义用户认证配置
100+
101+
> 需要继承UserDetailsService接口类,并实现其中的函数
102+
> 对于User对象,权限字符串不可直接给null值
103+
> 在UserService中,用户名验证成功后,user对象会在下一步,进行密码验证
104+
105+
{% highlight java %}
106+
@Component
107+
public class UserService implements UserDetailsService {
108+
109+
@Autowired
110+
private AdminMapper adminMapper;
111+
112+
@Override
113+
public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
114+
//根据用户名查询信息
115+
QueryWrapper<AdminEntity> sql = new QueryWrapper<>();
116+
sql.eq("username",s);
117+
AdminEntity adminEntity = adminMapper.selectOne(sql);
118+
if(adminEntity == null){
119+
throw new UsernameNotFoundException("用户名或密码错误!");
120+
}
121+
//用户验证通过,构建User对象,进行密码验证
122+
//用户权限不能为空,先生成默认权限
123+
List authz = AuthorityUtils.commaSeparatedStringToAuthorityList("admin");
124+
User user = new User(adminEntity.getUsername(), adminEntity.getPassword(), authz);
125+
126+
return user;
127+
}
128+
}
129+
{% endhighlight %}
130+
131+
### 自定义MD5密码验证
132+
133+
> 自定义密码验证类,需要继承PasswordEncoder接口类
134+
> 继承后,实现encode和matches函数,前者是加密,后者是匹配
135+
> 代码中得MD5是自定义的工具类
136+
137+
{% highlight java %}
138+
public class MD5Password implements PasswordEncoder {
139+
140+
@Override
141+
public String encode(CharSequence charSequence) {
142+
return MD5.encode(charSequence.toString());
143+
}
144+
145+
@Override
146+
public boolean matches(CharSequence charSequence, String s) {
147+
return MD5.valid(charSequence.toString(),s);
148+
}
149+
}
150+
{% endhighlight java %}
151+
152+
### 自定义相应
153+
154+
> 大部分业务逻辑中,对于成功和失败的响应都需要有固定的格式,security当然也提供了这种功能
155+
> 成功响应继承AuthenticationSuccessHandler接口类
156+
> 失败响应继承AuthenticationFailureHandler接口类
157+
158+
{% highlight java %}
159+
//成功
160+
public class SuccessHandler implements AuthenticationSuccessHandler {
161+
162+
@Override
163+
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
164+
R r = R.ok().message("登录成功!");
165+
//转为jsonobject对象
166+
JsonObject jsonObject = JsonUtil.toJsonObject(r);
167+
//设置返回字符串和返回类型
168+
httpServletResponse.setCharacterEncoding("utf-8");
169+
httpServletResponse.setContentType("application/json;charset=utf-8");
170+
//打印字符串
171+
PrintWriter out = null;
172+
out = httpServletResponse.getWriter();
173+
out.write(jsonObject.toString());
174+
out.close();
175+
}
176+
}
177+
178+
//失败
179+
public class FailureHandler implements AuthenticationFailureHandler {
180+
181+
@Override
182+
public void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
183+
R r = R.error().code(200).message(用户名或密码错误!");
184+
//转为jsonobject对象
185+
JsonObject jsonObject = JsonUtil.toJsonObject(r);
186+
//设置返回字符串和返回类型
187+
httpServletResponse.setCharacterEncoding("utf-8");
188+
httpServletResponse.setContentType("application/json;charset=utf-8");
189+
//打印字符串
190+
PrintWriter out = null;
191+
out = httpServletResponse.getWriter();
192+
out.write(jsonObject.toString());
193+
out.close();
194+
}
195+
}
196+
{% endhighlight %}
197+
198+
### 整合上述配置
199+
200+
{% highlight java %}
201+
@Configuration
202+
@EnableWebSecurity
203+
public class CustomerConfiguration extends WebSecurityConfigurerAdapter {
204+
205+
@Override
206+
protected void configure(HttpSecurity http) throws Exception {
207+
http.formLogin()
208+
//登录页面
209+
.loginPage("/login/index")
210+
//登录请求url,同页面中ajax请求url
211+
.loginProcessingUrl("/login/index")
212+
//设置自定义成功返回
213+
.successHandler(new SuccessHandler())
214+
//设置自定义失败返回
215+
.failureHandler(new FailureHandler())
216+
.permitAll();
217+
218+
http.authorizeRequests()
219+
//不需要认证就能访问的url
220+
.antMatchers("/login/**","/static/**")
221+
.permitAll()
222+
//其余url都需要经过认证
223+
.anyRequest().authenticated();
224+
225+
//禁用csrf保护
226+
http.csrf().disable();
227+
}
228+
229+
/**
230+
* 配置自定义加密类
231+
* @return
232+
*/
233+
@Bean
234+
public PasswordEncoder passwordEncoder(){
235+
return new MD5Password();
236+
}
237+
}
238+
{% endhighlight %}
239+
240+
配置完成后的使用效果
241+
242+
![](/images/2020-12-26-1.jpg)
243+
244+
![](/images/2020-12-26-2.jpg)
245+
246+
两次请求的结果都是由security中的自定义配置中返回

images/2020-12-04-1.jpg

822 KB
Loading

images/2020-12-04-2.jpg

11.6 KB
Loading

images/2020-12-04-3.png

54.9 KB
Loading

images/2020-12-26-1.jpg

537 KB
Loading

images/2020-12-26-2.jpg

536 KB
Loading

0 commit comments

Comments
 (0)