一、在pom.xml导入SpringSecurity对应的jar包
1. 在父工程中引入spring-security依赖
<properties>
......
<!-- 控制引入spring-security依赖的版本号 -->
<spring-security.version>4.2.10.RELEASE</spring-security.version>
</properties>
<dependencies>
......
<!-- spring-security依赖包 -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>${spring-security.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>${spring-security.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-taglibs</artifactId>
<version>${spring-security.version}</version>
</dependency>
</dependencies>
2. 在子工程(我这里是在Service层与Web层引入)中声明spring-security依赖
<dependencies>
......
<!-- 引入spring-security依赖包,不需要指明版本号,由父工程控制 -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-taglibs</artifactId>
</dependency>
</dependencies>
二、在web工程中的web.xml配置SpringSecurity对应的过滤器(这里需要将Spring容器与SpringMVC容器整合成一个容器,即SpringMVC容器)
<!-- springSecurity过滤器 -->
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
三、编写SpringSecurity配置类
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
//import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.access.AccessDeniedHandler;
//SpringSecurity的配置文件
@Configuration //表示这个一个SpringSecurity的配置文件
@EnableWebSecurity //开启 Spring Security 注解
@EnableGlobalMethodSecurity(prePostEnabled=true) //开启全局的细粒度方法级别权限控制功能
public class AtCrowdFundingSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
UserDetailsService userDetailsService;
// @Autowired
// private PasswordEncoder passwordEncoder;
//基本配置
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
//开放权限("welcome.jsp"只能访问这个页面,不能访问这个页面的所有转发和请求,"/welcome.jsp"则可以)
.antMatchers("/static/**","welcome.jsp","/login").permitAll()
//除以上资源都得认证后才能访问
.anyRequest().authenticated();
//如果没有登录就去访问没有权限的页面或者请求,指定跳转到自定义登录页面
http.formLogin().loginPage("/login")
//自定义表单的action请求的路径
.loginProcessingUrl("/doLogin")
//自定义表单的username的name
.usernameParameter("loginacct")
//自定义表单的password的name
.passwordParameter("userpswd")
.defaultSuccessUrl("/main");
//禁用CSRF
http.csrf().disable();
http.exceptionHandling().accessDeniedHandler(new AccessDeniedHandler() {
@Override
public void handle(HttpServletRequest request, HttpServletResponse response,
AccessDeniedException accessDeniedException) throws IOException, ServletException {
String header = request.getHeader("X-Requested-With");
//如果有该值则表示这是一个AJAX异步请求
if("XMLHttpRequest".equals(header)) {
response.getWriter().print("403");
}else {//如果没有该值则表示这是一个同步请求
request.getRequestDispatcher("/WEB-INF/jsp/admin/error403.jsp").forward(request, response);
}
}
});
}
//认证和授权的配置
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//基于数据库的用户认证,需要实现UserDetailsService类
auth.userDetailsService(userDetailsService)
//使用BCrypt加密算法
.passwordEncoder(new BCryptPasswordEncoder());
//使用自定义的加密算法(MD5)
// .passwordEncoder(passwordEncoder);
}
}
四、编写UserDetailsService实现类
import java.util.HashSet;
import java.util.List;
import java.util.Set;
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.Component;
import com.atguigu.atcrowdfunding.bean.TAdmin;
import com.atguigu.atcrowdfunding.bean.TAdminExample;
import com.atguigu.atcrowdfunding.bean.TPermission;
import com.atguigu.atcrowdfunding.bean.TRole;
import com.atguigu.atcrowdfunding.mapper.TAdminMapper;
import com.atguigu.atcrowdfunding.mapper.TPermissionMapper;
import com.atguigu.atcrowdfunding.mapper.TRoleMapper;
//基于数据库的用户认证信息实现类
@Component
public class MyUserDetailsServiceImpl implements UserDetailsService{
@Autowired
private TAdminMapper adminMapper;
@Autowired
private TRoleMapper roleMapper;
@Autowired
private TPermissionMapper permissionMapper;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
//首先根据username去查询数据库是否有该用户
TAdminExample example = new TAdminExample();
example.createCriteria().andLoginacctEqualTo(username);
List<TAdmin> admins = adminMapper.selectByExample(example);
TAdmin admin = null;
if(admins!=null&&admins.size()==1) {
admin = admins.get(0);
}
//根据adminId去查询该用户拥有的所有角色信息
Integer adminId = admin.getId();
List<TRole> roles = roleMapper.queryRolesByAdminId(adminId);
//根据adminId去查询该用户拥有的所有权限信息
List<TPermission> permissions = permissionMapper.queryPermissionsByAdminId(adminId);
Set<GrantedAuthority> authorities = new HashSet<GrantedAuthority>();
//将查询的所有角色名字都放入set集合中,注意要加前缀"ROLE_"
for (TRole role : roles) {
authorities.add(new SimpleGrantedAuthority("ROLE_"+role.getName()));
}
//将查询的所有权限名字都放入set集合中
for (TPermission permission : permissions) {
authorities.add(new SimpleGrantedAuthority(permission.getName()));
}
//返回一个SpringSecurity框架的User类,该类包含了该用户的用户名和加密的密码以及拥有的角色和权限
return new User(admin.getLoginacct(), admin.getUserpswd(), authorities);
}
}
五、编写PasswordEncoder实现类(使用MD5加密)
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;
import com.atguigu.atcrowdfunding.util.MD5Util;
@Component
public class MyPasswordEncoderImpl implements PasswordEncoder{
//使用MD5对前端传来的明文密码加密
@Override
public String encode(CharSequence rawPassword) {
String password = MD5Util.digest(rawPassword.toString());
return password;
}
//判断加密后的密文与数据库中的密文是否一样
@Override
public boolean matches(CharSequence rawPassword, String encodedPassword) {
String uiPassword = encode(rawPassword);
String dbPassword = encodedPassword;
return dbPassword.equals(uiPassword);
}
}
六、在Controller对应的映射请求中加上@PreAuthorize(xxx)注解即可
//添加用户
@ResponseBody
@RequestMapping(value="/role/addRole")
@PreAuthorize("hasRole('PM - 项目经理') AND hasAuthority('user:add')")
public String saveRole(TRole role) {
int i = roleService.saveRole(role);
if(i!=0) {
return "OK";
}else {
return "False";
}
}
注:如需要将用户姓名在前端显示,只要在前端加上<security:authentication property=“name”/>即可
如需要在前端根据权限显示对应的按钮、图片等内容时,只需要在标签外边包上<security:authorize access="xxx "></security:authorize>标签,如下图所示,不过都需要先引入springsecurity标签库。
<!-- 权限控制 -->
<!-- hasRole('PM - 项目经理') AND hasRole('SE - 软件工程师'):必须含有这两个角色才能访问 -->
<!-- hasRole('PM - 项目经理') OR hasRole('SE - 软件工程师'):有这两个角色中的一个就能访问,也可以同时拥有这两个角色 -->
<!-- hasRole('PM - 项目经理') AND hasAuthority('user:add'):必须含有该角色和该权限才能访问 -->
<!-- hasRole('PM - 项目经理') OR hasAuthority('user:add'):有这个角色和这个权限中的一个就能访问,也可以同时拥有这个角色和该权限 -->
<security:authorize access="hasRole('PM - 项目经理') OR hasRole('SE - 软件工程师') ">
<button id="addBtn" type="button" class="btn btn-primary" style="float: right;">
<i class="glyphicon glyphicon-plus"></i> 新增
</button>
</security:authorize>