Java安全框架(一):Spring Security基础
Java安全框架(二):Spring Security工作原理
Java安全框架(三):JWT(Json Web Token)
一、依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.0</version>
</dependency>
二、SecurityConfig配置
1、登录认证授权配置
(1)指定哪些网页需要登录,哪些不需要(直接配置)
(2)配置登录页面拦截器,以便自定义登录页面(继承重写)
(3)配置请求过滤,默认是session验证,需要重新配置token验证(继承重写)
2、加密配置
(1)实现UserDetailService重写loadUserByUsername方法,用于将数据库中用户角色权限等信息放入安全上下文。(实现重写)
(2)重写加密算法,使用md5进行加密(继承重写)
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UserDetailsService myCustomUserService;
@Autowired
private MyPasswordEncoder myPasswordEncoder;
@Override
protected void configure(HttpSecurity http) throws Exception {
http
//关闭跨站请求防护
.cors().and().csrf().disable()
//允许不登陆就可以访问的方法,多个用逗号分隔
.authorizeRequests().antMatchers("/test").permitAll()
//其他的需要授权后访问
.anyRequest().authenticated()
.and()
//增加登录拦截
.addFilter(new JWTLoginFilter(authenticationManager()))
//增加请求过滤
.addFilter(new JWTAuthenticationFilter(authenticationManager()))
// 前后端分离是无状态的,所以暫時不用session,將登陆信息保存在token中。
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
@Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
//覆盖UserDetailsService类
auth.userDetailsService(myCustomUserService)
//覆盖默认的密码验证类
.passwordEncoder(myPasswordEncoder);
}
}
三、实现配置文件中的自定义类
1、配置登录拦截器
作用:Security有自己的登录页面,配置拦截可以实现自定义登录
做法:自定义JWTLoginFilter,继承UsernamePasswordAuthenticationFilter 接口,拦截登录并做自定义登录(将token等以json格式返回,方便做前后端分离和单点登录等)。
public class JWTLoginFilter extends UsernamePasswordAuthenticationFilter {
}
2、配置请求过滤
作用:对controller中需要登录才能访问的方法进行拦截(token验证),返回json提示信息。
做法:JWTAuthenticationFilter 类继承BasicAuthenticationFilter 接口,重写请求过滤
public class JWTAuthenticationFilter extends BasicAuthenticationFilter {
}
3、配置读取数据库信息放入安全上下文
作用:可以根据token中传来的信息,从数据库查询用户角色权限,放入安全上传下文,便于后续认证授权
做法:实现UserDetailsService,重写loadUserByUsername方法
@Component
public class MyCustomUserService implements UserDetailsService {
/**
* 登陆验证时,通过username获取用户的所有权限信息
* 并返回UserDetails放到spring的全局缓存SecurityContextHolder中,以供授权器使用
*/
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
//在这里可以自己调用数据库,对username进行查询,看看在数据库中是否存在
return myUserDetail;
}
}
备注:此处从数据库查的用户权限和角色,放入了安全上下文,因此,就可以直接在controller需要权限认证的方法上加权限限制的注解了。
4、重写加密算法
作用:默认是不加密的,用户密码不加密存入数据库是很可怕的,因此需要配置密码加密,使用md5加密(不可逆的)
做法:实现PasswordEncoder实现加密和密码验证的方法,并编写md5加密算法
- 自定义加密
public class EmcsPasswordEncoder implements PasswordEncoder {
@Override
public boolean matches(CharSequence rawPassword, String encodedPassword) {
return encodedPassword.equals(Md5Encoder.encode((String)rawPassword));
}
@Override
public String encode(CharSequence rawPassword) {
return Md5Encoder.encode((String)rawPassword);
}
}
- 编写md5加密工具类
public class Md5Encoder {
private static final String SALT = "yang";
public static String encode(String password) {
password = password + SALT;
MessageDigest md5 = null;
try {
md5 = MessageDigest.getInstance("MD5");
} catch (Exception e) {
throw new RuntimeException(e);
}
char[] charArray = password.toCharArray();
byte[] byteArray = new byte[charArray.length];
for (int i = 0; i < charArray.length; i++)
byteArray[i] = (byte) charArray[i];
byte[] md5Bytes = md5.digest(byteArray);
StringBuffer hexValue = new StringBuffer();
for (int i = 0; i < md5Bytes.length; i++) {
int val = ((int) md5Bytes[i]) & 0xff;
if (val < 16) {
hexValue.append("0");
}
hexValue.append(Integer.toHexString(val));
}
return hexValue.toString();
}
}
以上配置完成就实现了前后端分离、md5加密、token验证、权限控制。
四、用户鉴权工具类
作用:在项目中,搭建好以上框架后,根据上下文可以获取到用户角色和权限,为了方便鉴权则可以编写一个配置类,需要鉴权的时候直接调用该工具类进行鉴权。
public class UserUtils {
public static final String SYS = "sys";
public static Boolean isSys() {
if (!UserUtils.isLogin()) {
return false;
}
return StringUtils.contains(getRole(), SYS);
}
public static String getRole() {
GrantedAuthority[] authorities = getAuth().getAuthorities().toArray(new GrantedAuthority[1]);
if (authorities.length > 0) {
for (GrantedAuthority authority : authorities) {
String authorityName = authority.getAuthority();
if (authorityName.startsWith(RoleConfig.ROLE_PREFIX)) {
return authorityName.substring(5);
}
}
}
return null;
}
}
五、单点登录配置
实质:token验证
1、添加依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.0</version>
</dependency>
2、增加Security配置文件
由于单点登录是通过token验证的,所以,就没有了登录拦截和密码校验步骤,只需要请求过滤即可
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
//关闭跨站请求防护
.cors().and().csrf().disable()
//允许不登陆就可以访问的方法,多个用逗号分隔
.authorizeRequests()
//.antMatchers("/index").permitAll()
//其他的需要授权后访问
.anyRequest().authenticated()
.and()
//增加请求过滤
.addFilterBefore(new JWTAuthenticationFilter(),
UsernamePasswordAuthenticationFilter.class)
// 前后端分离是无状态的,所以暫時不用session,將登陆信息保存在token中。
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
}
3、token验证
需要根据JWT的算法解析token
public class JWTAuthenticationFilter extends BasicAuthenticationFilter {
}
以上就是单点登录实现的流程。




