关于spring security的若干事情
最近项目在做到spring security这块,要进行权限的管理和设置,然后本人开始了一段自学的过程,这么说把spring实战里面的例子都是比较典型的,但不是很实用。
后来在网上找了一些教程,就自己做了一下简单的权限的管理。因为是网上这找那找的教程,所以无法感谢很多给我提供帮助的博友们。好吧,begin it。
首先要明确为什么要权限,为什么要用spring security?
在一个系统中的权限是比较重要的一部分,用来区分不同操作,很简单的一个例子,优酷的普通用户和会员就是一个角色不同权限不同的例子。
为什么要用spring security?
因为spring security可以帮我们很好的管理权限和角色,如果不用spring security我们完全可以在我们需要区分不同角色和权限的位置,马上发一个ajax去后台查询角色和权限,但spring都帮你包装好了,为什么不直接用呢 ?
ok
jar包什么的网上一搜就有,看一下spring security的配置,跟spring 配置一样分为两种 xml和javaconfig配置,因为在我的项目中是用java配置的spring,所以我也就直接用java 配置了spring security。
配置还是比较简单的,在你的config下配置一个SecurityConfig.java和SecurityWebApplicationInitializer.java 这两个文件,第一个文件我大概知道用途,第二个文件到现在我也不是清楚。
这是具体的代码
SecurityWebApplicationInitializer .java
package project.config;
import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer;
/**
* @author monster
* 2017年7月20日上午9:53:53
*/
public class SecurityWebApplicationInitializer extends AbstractSecurityWebApplicationInitializer {
}
SecurityConfig.java
package project.config;
import java.util.Date;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.servlet.configuration.EnableWebMvcSecurity;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.access.DelegatingAccessDeniedHandler;
import project.PM.handle.MyAccessDeniedHandler;
import project.PM.service.CustomUserService;
import project.PM.serviceImpl.CustomUserServiceImpl;
@SuppressWarnings("deprecation")
@Configuration
@EnableWebMvcSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
//注入类
@Bean
MyAccessDeniedHandler myAccessDeniedHandler(){ //注册UserDetailsService 的bean
return new MyAccessDeniedHandler();
}
@Bean
UserDetailsService customUserService(){ //注册UserDetailsService 的bean
return new CustomUserServiceImpl();
}
//指定用户的登录时候需要进行用户名和密码审核的类为customUserService
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//暂时使用基于内存的AuthenticationProvider 这种方式是spring实战中介绍的基于内存的用户名和密码认证,相当于是固定用户的,那我们需要的是从数据库中读到的用户列表而不是在这个内存中写死的。
// auth.inMemoryAuthentication()
// .withUser("user").password("123456").roles("USER").and()
// .withUser("admin").password("15659987939").roles("USER","ADMIN");
//指定用户认证类
auth.userDetailsService(customUserService());
}
//配置静态资源访问
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/static/**");
}
// @Override
// protected void configure(HttpSecurity http) throws Exception {
// // TODO Auto-generated method stub
// super.configure(http);
// }
@Override
protected void configure(HttpSecurity http) throws Exception {
//这里进行配置spring security, .antMatchers()是一种表达式,用来进行匹配。.permitAll()是允许所有人访问的意思,而access()是指需要后面的角色或者权限才接受访问。.csrf().disable()是关闭跨站攻击(其实我也不是很懂)。
http.authorizeRequests()
.antMatchers("/login","/isexistuser","/sendemail","/resetuserpwd","/webjars/**","/resources/**")
.permitAll()
.antMatchers("/um/**","/am/**","/cm/**")
.access("!hasRole('ROLE_USER')")
.anyRequest().authenticated() //任何请求,登录后可以访问
.and()
.csrf().disable()
//下面是一个登录流程的配置,指定formlogin,其实loginpage为/login,默认成功跳转页面为/main,true表示默认跳转。失败页面为/login?error,登出页面为/logout。
//特别要注意的是这个跳转的都是/main,指定拦截,也就是说要在controller下有对应的拦截@RequestMapping,否则不行,一开始还以为是直接找的main.jsp后来才知道是找的controller的requestmapping
.formLogin()
.loginPage("/login")
.defaultSuccessUrl("/main",true)
.failureUrl("/login?error")
.permitAll() //登录页面用户任意访问
.and()
.logout()
.logoutUrl("/logout")
.logoutSuccessUrl("/login")
.permitAll(); //注销行为任意访问
//配置403,配置权限限制是跳转的页面
http.exceptionHandling().accessDeniedHandler(myAccessDeniedHandler());
}
}
CustomUserServiceImpl .java
package project.PM.serviceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import project.PM.mapper.ARoleMapper;
import project.PM.mapper.PermissionMapper;
import project.PM.mapper.RoleMapper;
import project.PM.mapper.UserMapper;
import project.PM.model.SysARole;
import project.PM.model.SysPermission;
import project.PM.model.SysRole;
import project.PM.model.SysRoleUser;
import project.PM.model.SysUser;
import project.PM.service.SysARoleService;
import project.PM.service.SysUserService;
import java.io.UnsupportedEncodingException;
import java.security.Permission;
import java.util.ArrayList;
import java.util.List;
@Service
//UserDetailsService是spring security的配置用户验证的接口
public class CustomUserServiceImpl implements UserDetailsService { //自定义UserDetailsService 接口
//这里的service和mapper写得有点乱,见谅。。新手,对mybatis操作,从数据库中拿到用户列表,进行匹配
@Autowired
private SysUserService sysUserService;
@Autowired
private ARoleMapper aRoleMapper;
@Autowired
private PermissionMapper permissionMapper;
@Autowired
private RoleMapper roleMapper;
//spring security 默认会进入这个函数
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
// TODO Auto-generated method stub
//进行用户名的编码格式转码,因为spring security会默认转成ios-8859-1
System.out.println("w");
//spring security 会自动转成iso-8859-1,CSRF Token对servlet的拦截
try {
username=new String(username.getBytes("iso-8859-1"),"UTF-8");
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//根据用户名进行获取用户
SysUser user = sysUserService.getUserByUserName(username);
if (user != null) {
SysRole sysRole = roleMapper.findRoleByUserId(user.getId());
// 权限集合 成功登录用来返回
List<GrantedAuthority> grantedAuthorities = new ArrayList<GrantedAuthority>();
if (sysRole != null) {
// 获取角色名字
String roleName = sysRole.getName();
//先注入是否管理员和用户
GrantedAuthority grantedAuthority1 = new SimpleGrantedAuthority(String.valueOf(sysRole.getName()));
grantedAuthorities.add(grantedAuthority1);
List<SysPermission> permissions ;//权限集合
//用户的权限注入
if (roleName.equals("ROLE_USER"))
{
SysRoleUser sysRoleUser = roleMapper.getUserRoleRelationByUid(user.getId());
int aroleId = sysRoleUser.getSys_arid();
// 普通用户的详细权限
permissions = permissionMapper.getPermissionsByARoleId(aroleId);
}else{
//管理员 完全权限注入
permissions = permissionMapper.getAllPermissions();
}
//将权限注入
for (SysPermission permission : permissions) {
if (permission != null && permission.getName() != null) {
String temp=permission.getOperate_id()+permission.getDescription();
GrantedAuthority grantedAuthority = new SimpleGrantedAuthority(temp);
// 1:此处将权限信息添加到 GrantedAuthority
// 对象中,在后面进行全权限验证时会使用GrantedAuthority 对象。
grantedAuthorities.add(grantedAuthority);
}
}
return new User(user.getUsername(), user.getPassword(), grantedAuthorities);
}else {
throw new UsernameNotFoundException("admin: " + username + " didn't have any authority.");
}
} else {
throw new UsernameNotFoundException("admin: " + username + " didn't have any authority.");
}
}
}
在页面中使用 <security:authorize access="hasRole('ROLE_USER')" > <span>这是角色为ROLE_USER的用户才能看见</span> </security:authorize>进行角色管理
使用<security:authorize access="hasAuthority('TakePhoto')" > <span>这是拥有权限为TakePhoto的用户才能看见</span> </security:authorize>进行权限管理,根据角色权限是否拥有来决定 security:authorize标签中的内容是否显示,使用这个标签需要引入spring security的tags库,网上搜一下~~
最后在补充一下登录页面吧。
<c:url value="/login" var="success"/>
<form action="${success}" method="post" onsubmit="false" accept-charset="utf-8">
<div class="form-group has-feedback">
<input style="border-radius:5px;" name="username" type="text" class="form-control" placeholder="用户">
<span class="glyphicon glyphicon-user form-control-feedback"></span>
</div>
<div class="form-group has-feedback">
<input style="border-radius:5px;" name="password" type="password" class="form-control" placeholder="密码">
<span class="glyphicon glyphicon-lock form-control-feedback"></span>
</div>
<div class="row">
<div class="col-xs-12">
<button type="submit" style="border-radius:5px;" class="btn btn-primary btn-block btn-flat">登录</button>
</div>
</div>
</div>
</form>
如果开启了csrf跨站攻击,需要在表单内添加跨站安全防护。
<input type="hidden"
name="${_csrf.parameterName}"
value="${_csrf.token}" />
可以看出,使用方式越来越简单,程序员越来越傻,不知道是好事,还是坏事。。