目录
1、加入SpringSecurity依赖
<!-- SpringSecurity对Web应用进行权限管理 -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>4.2.10.RELEASE</version>
</dependency>
<!-- SpringSecurity配置 -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>4.2.10.RELEASE</version>
</dependency>
<!-- SpringSecurity标签库 -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-taglibs</artifactId>
<version>4.2.10.RELEASE</version>
</dependency>
2、 web.xml加入SpringSecurity控制权限的Filter
SpringSecurity使用的是过滤器Filter而不是拦截器Interceptor,意味着SpringSecurity能够管理的不仅仅是SpringMVC中的handler请求,还包含Web应用中所有请求。比如:项目中的静态资源也会被拦截,从而进行权限控制。
<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>
3、加入配置类
继承WebSecurityConfigurerAdapter
类,会默认使用该类的配置,有需要再修改该类的配置
@EnableWebSecurity
:开启security
com.wzw.security.config.WebAppSecurityConfig
@Configuration
@EnableWebSecurity
public class WebAppSecurityConfig extends WebSecurityConfigurerAdapter {
}
4、启动项目
启动项目会出现原本没有的登录页面,这个是security的默认登录界面,所有界面都访问不了,包括静态资源界面
5、授权请求
以下的登录只是一个跳转到另一个页面,不是真的登录,所以会被拦截。
5.1 定义授权规则
首先重写configure(HttpSecurity http)
这个方法,定义授权规则,
5.2放行首页和静态资源
antMatchers
可以写多个。也可以写多个地址antMatchers(参数1,参数2...)
//定义授权规则
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests() //对请求进行授权
.antMatchers("/index.jsp","/layui/**").permitAll() //设置这个地址所有人都可以访问
.antMatchers("/index1.jsp").hasRole("admin") //设置这个地址只有admin这个角色才能访问
.anyRequest().authenticated(); //除了配置过的antMatchers(),其它的访问地址都要认证以后才能访问
}
上面配置了index,jsp可以访问,访问成功,但是点击登录后,跳转了,但是因为没有授权,所以被拒绝了
5.3未授权跳转到登录页
上面页面没有写登录失败去往的地址,直接报错403,这里通过formLogin().loginPage("/index.jsp").permitAll()
设置未授权去的页面。
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests() //对请求进行授权
.antMatchers("/index.jsp","/layui/**").permitAll() //设置这个地址所有人都可以访问
.antMatchers("/index1.jsp").hasRole("admin") //设置这个地址只有admin这个角色才能访问
.anyRequest().authenticated() //除了配置过的antMatchers(),其它的访问地址都要认证以后才能访问
.and()
.formLogin() //指定使用表单作为登录方式
.loginPage("/index.jsp").permitAll(); //如果没有登录,去往指定的地址,所有人都可以访问
}
这里的登录是假的,所以点击登录其实只是一个页面跳转,因为去到的页面要登录才能访问,所以来到的页面是loginPage("/index.jsp")
指定的页面。
附:
5.4定义认证规则
重写configure(AuthenticationManagerBuilder auth)
方法,
5.5 设置登录的账号密码
inMemoryAuthentication()
:基于内存的用户存储
withUser("tom").password("123")
:用户名和密码
roles("admin")
:设置拥有的角色
//定义认证规则
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser("tom").password("123") //指定登录的账号和密码
.roles("admin"); //必须设置角色和权限,否则报错Cannot pass a null GrantedAuthority collection
}
设置多个角色用and()连接
auth
.inMemoryAuthentication().
.withUser("tom").password("123").roles("admin")
.and().withUser("lisi").password("123").roles("VIP2","VIP3")
.and().withUser("wangwu").password("111").roles("VIP1","VIP3");
5.6 退出登录
and()
logout()
:开启用户退出登录功能
logoutUrl("/my/add/logout")
:设置退出登录的地址
logoutSuccessUrl("/index.jsp")
:退出登录以后去的地址
//定义授权规则
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests() //对请求进行授权
.antMatchers("/index.jsp","/layui/**").permitAll() //设置这个地址所有人都可以访问
.antMatchers("/index1.jsp").hasRole("admin") //设置这个地址只有admin这个角色才能访问
.anyRequest().authenticated() //除了配置过的antMatchers(),其它的访问地址都要认证以后才能访问
.and()
.formLogin() //指定使用表单作为登录方式
.loginPage("/index.jsp").permitAll() //如果没有登录,去往指定的地址,所有人都可以访问
.defaultSuccessUrl("/main.html") //登录成功后去往的地址
.usernameParameter("loginacct") //设置登录的表单的用户名的name属性值,默认是username
.passwordParameter("credential") //设置登录的表单的密码的name属性值,默认是password
.and()
.logout() //开启用户退出登录功能
.logoutUrl("/my/add/logout") //设置退出登录的地址
.logoutSuccessUrl("/index.jsp"); //退出登录以后去的地址
}
5.7 设置角色要求
antMatchers("/level1/**").hasRole("admin1")
//设置这个地址只有admin1这个角色才能访问
antMatchers("/level2/**").hasRole("admin2")
//设置这个地址只有admin2这个角色才能访问
antMatchers("/level3/**").hasRole("admin3")
//设置这个地址只有admin3这个角色才能访问
//定义授权规则
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests() //对请求进行授权
.antMatchers("/index.jsp","/layui/**").permitAll() //设置这个地址所有人都可以访问
.antMatchers("/level1/**").hasRole("admin1") //设置这个地址只有admin1这个角色才能访问
.antMatchers("/level2/**").hasRole("admin2") //设置这个地址只有admin2这个角色才能访问
.antMatchers("/level3/**").hasRole("admin3") //设置这个地址只有admin3这个角色才能访问
.anyRequest().authenticated() //除了配置过的antMatchers(),其它的访问地址都要认证以后才能访问
.and()
.formLogin() //指定使用表单作为登录方式
.loginPage("/index.jsp").permitAll() //如果没有登录,去往指定的地址,所有人都可以访问
.defaultSuccessUrl("/main.html") //登录成功后去往的地址
.usernameParameter("loginacct") //设置登录的表单的用户名的name属性值,默认是username
.passwordParameter("credential") //设置登录的表单的密码的name属性值,默认是password
.and()
.logout() //开启用户退出登录功能
.logoutUrl("/my/add/logout") //设置退出登录的地址
.logoutSuccessUrl("/index.jsp"); //退出登录以后去的地址
}
roles("admin1","admin2")
:设置拥有角色admin1和admin2
//定义认证规则
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser("tom").password("123") //指定登录的账号和密码
.roles("admin1","admin2"); //必须设置角色和权限,否则报错Cannot pass a null GrantedAuthority collection
}
5.8指定页面代替403
没有权限会报403
为了更好的体验,指定一个没有权限去往的页面
//定义授权规则
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests() //对请求进行授权
.antMatchers("/index.jsp","/layui/**").permitAll() //设置这个地址所有人都可以访问
.antMatchers("/level1/**").hasRole("admin1") //设置这个地址只有admin1这个角色才能访问
.antMatchers("/level2/**").hasRole("admin2") //设置这个地址只有admin2这个角色才能访问
.antMatchers("/level3/**").hasRole("admin3") //设置这个地址只有admin3这个角色才能访问
.anyRequest().authenticated() //除了配置过的antMatchers(),其它的访问地址都要认证以后才能访问
.and()
.formLogin() //指定使用表单作为登录方式
.loginPage("/index.jsp").permitAll() //如果没有登录,去往指定的地址,所有人都可以访问
.defaultSuccessUrl("/main.html") //登录成功后去往的地址
.usernameParameter("loginacct") //设置登录的表单的用户名的name属性值,默认是username
.passwordParameter("credential") //设置登录的表单的密码的name属性值,默认是password
.and()
.logout() //开启用户退出登录功能
.logoutUrl("/my/app/logout") //设置退出登录的地址
.logoutSuccessUrl("/index.jsp") //退出登录以后去的地址
.and()
.exceptionHandling()
.accessDeniedPage("/to/no/auth/page.html"); //没有权限去往的地址
}
.and()
.exceptionHandling()
.accessDeniedPage("/to/no/auth/page.html");
accessDeniedPage也可以替换为accessDeniedHandler,并且自定义消息message
.and()
.exceptionHandling()
// .accessDeniedPage("/to/no/auth/page.html"); //没有权限去往的地址
.accessDeniedHandler(new AccessDeniedHandler() {
@Override
public void handle(HttpServletRequest request, HttpServletResponse response,
AccessDeniedException accessDeniedException) throws IOException, ServletException {
request.setAttribute("message", accessDeniedException.getMessage());
request.getRequestDispatcher("/WEB-INF/views/no_auth.jsp").forward(request, response);
}
});
5.9 记住我-内存版
定义授权规则中开启记住我功能and().rememberMe()
,重启服务就失效了
//定义授权规则
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests() //对请求进行授权
.antMatchers("/index.jsp","/layui/**").permitAll() //设置这个地址所有人都可以访问
.antMatchers("/level1/**").hasRole("admin1") //设置这个地址只有admin1这个角色才能访问
.antMatchers("/level2/**").hasRole("admin2") //设置这个地址只有admin2这个角色才能访问
.antMatchers("/level3/**").hasRole("admin3") //设置这个地址只有admin3这个角色才能访问
.anyRequest().authenticated() //除了配置过的antMatchers(),其它的访问地址都要认证以后才能访问
.and()
.formLogin() //指定使用表单作为登录方式
.loginPage("/index.jsp").permitAll() //如果没有登录,去往指定的地址,所有人都可以访问
.defaultSuccessUrl("/main.html") //登录成功后去往的地址
.usernameParameter("loginacct") //设置登录的表单的用户名的name属性值,默认是username
.passwordParameter("credential") //设置登录的表单的密码的name属性值,默认是password
.and()
.logout() //开启用户退出登录功能
.logoutUrl("/my/app/logout") //设置退出登录的地址
.logoutSuccessUrl("/index.jsp") //退出登录以后去的地址
.and()
.exceptionHandling()
// .accessDeniedPage("/to/no/auth/page.html"); //没有权限去往的地址
.accessDeniedHandler(new AccessDeniedHandler() {
@Override
public void handle(HttpServletRequest request, HttpServletResponse response,
AccessDeniedException accessDeniedException) throws IOException, ServletException {
request.setAttribute("message", accessDeniedException.getMessage());
request.getRequestDispatcher("/WEB-INF/views/no_auth.jsp").forward(request, response);
}
})
.and()
.rememberMe() //开启记住我功能
;
将登录表单中的checkbox的name设置为remember-me
<input type="checkbox" name="remember-me" lay-skin="primary" title="记住我">
如果想更换name属性名,可以用rememberMeParameter更换
.and()
.rememberMe() //开启记住我功能
.rememberMeParameter(“remember-me”) //可以不设置,不设置默认的表单的name属性名为remember-me,也可以更改
5.10 记住我-数据库版
将登陆信息存入数据库,服务重启也可以直接访问
5.10.1 导入pom依赖
<!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.12</version>
</dependency>
<!-- mysql驱动 -->
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>4.3.20.RELEASE</version>
</dependency>
5.10.2 配置数据源
<!-- 配置数据源 -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="username" value="root"></property>
<property name="password" value="root"></property>
<property name="url" value="jdbc:mysql://localhost:3306/security?useSSL=false"></property>
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
</bean>
<!-- jdbcTemplate-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
5.10.3创建数据库
CREATE DATABASE `security` CHARACTER SET utf8;
5.10.4 创建persistent_logins表
CREATE TABLE persistent_logins (
username VARCHAR (64) NOT NULL,
series VARCHAR (64) PRIMARY KEY,
token VARCHAR (64) NOT NULL,
last_used TIMESTAMP NOT NULL
);
5.11 查询数据库认证
之前的认证都是写死的数据在类中,接下来就是通过数据库认证。
5.11.1 自定义数据库查询方式
在以下路径新建AppUserDetailService类
package com.wzw.security.service;
@Service
public class AppUserDetailService implements UserDetailsService {
@Autowired
private JdbcTemplate jdbcTemplate;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
// 1.使用SQL语句根据用户名查询用户对象
String sql = "SELECT id,loginacct,userpswd,username,email,createtime FROM t_admin WHERE loginacct = ?";
// 2.获取查询结果
Map<String, Object> resultMap = jdbcTemplate.queryForMap(sql, username);
// 3.获取用户名、密码数据
String loginacct = resultMap.get("loginacct").toString();
String userpswd = resultMap.get("userpswd").toString();
// 4.创建权限列表
List<GrantedAuthority> list = AuthorityUtils.createAuthorityList("admin1", "admin2");
// 5.创建用户对象
User user = new User(loginacct, userpswd, list);
return user;
}
}
5.11.2 创建数据库表
create table t_admin
(
id int not null auto_increment,
loginacct varchar(255) not null,
userpswd char(32) not null,
username varchar(255) not null,
email varchar(255) not null,
createtime char(19),
primary key (id)
);
5.11.3 使用自定义UserDetailsService完成登录
package com.wzw.security.config;
@Autowired
private AppUserDetailService userDetailService;
// 定义认证规则
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
/* 这个是数据写死的验证
auth.inMemoryAuthentication().withUser("tom").password("123") // 指定登录的账号和密码
.roles("admin1"); // 必须设置角色和权限,否则报错Cannot pass a null GrantedAuthority collection
*/
//这个是直接通过数据库认证
auth.userDetailsService(userDetailService);
}
如果没删除之前之前数据库版记住我,就会报错,先删除之前的记住我版的数据库的数据
5.11.4 权限查询不到
在权限验证的时候
在这里会自动加上ROLE_,它的最终格式就是ROLE_admin1,ROLE_admin2,ROLE_admin3
在做自定义数据库查询的时候,就要加上ROLE_,否则权限验证不通过
5.12 密码加密
package com.wzw.security.config;
@Component
public class PasswordEncoderService implements PasswordEncoder {
//对原始明文密码加密
@Override
public String encode(CharSequence rawPassword) {
Assert.notNull(rawPassword,"rawPassword can not be null");
String password = CrowdFundingUtils.md5(rawPassword.toString());
return password;
}
//将明文密码和密文密码进行比较
@Override
public boolean matches(CharSequence rawPassword, String encodedPassword) {
Assert.notNull(rawPassword,"rawPassword can not be null");
String password = CrowdFundingUtils.md5(rawPassword.toString());
//对比明文密码加密以后和数据库的密文密码是否一致
return Objects.equals(password, encodedPassword);
}
}
5.13 自定义加密使用
package com.wzw.security.config;
@Autowired
private PasswordEncoder passwordEncoder;
// 定义认证规则
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
/* 这个是数据写死的验证
auth.inMemoryAuthentication().withUser("tom").password("123") // 指定登录的账号和密码
.roles("admin1"); // 必须设置角色和权限,否则报错Cannot pass a null GrantedAuthority collection
*/
//这个是直接通过数据库认证
auth.userDetailsService(userDetailService).passwordEncoder(passwordEncoder);
}
5.14设置权限验证
授权规则中设置权限验证,以下代表level2下的资源必须有权限loo2又有admin2这个角色才能访问
.antMatchers("/level2/**").hasRole("admin2") // 设置这个地址只有admin3这个角色才能访问
.antMatchers("/level2/**").hasAuthority("look2") //设置访问这个地址得有look2这个权限
在数据库查询那里,需要赋予权限,否则不能访问
package com.wzw.security.service;
// 4.创建权限列表
List<GrantedAuthority> list = AuthorityUtils.createAuthorityList("ROLE_admin1", "ROLE_admin2","look2");
这里的权限和角色卸载一起,但是权限不用带ROLE_的前缀,
6 数据库查询角色和权限
加入了scurity以后,由继承了WebSecurityConfigurerAdapter
类的方法控制登录和权限,在这里直接自定义认证和授权规则
6.1 自定义认证规则和授权规则
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true) // 表示启用全局方法权限管理功能。
public class CrowdfundingSecurityConfig extends WebSecurityConfigurerAdapter {
//这个就是下面的从自定义数据库认证类的接口
@Autowired
private UserDetailsService userDetailService;
//认证规则
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
/* auth.inMemoryAuthentication() //在内存中,不经过数据库
.withUser("zhangsan").password("123") //定义用户名和密码
.roles("admin1"); //定义一个用户角色 */
//通过数据库认证
auth.userDetailsService(userDetailService);
}
//授权规则
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/index.html", "/bootstrap/**",
"/css/**", "/fonts/**", "/img/**",
"/jquery/**", "/layer/**", "/script/**",
"/ztree/**","/testpage.html").permitAll() //设置权限,让这些地址没有登录就可以访问,permitAll():任何人都可以访问
.anyRequest().authenticated() //anyRequest()其它地址,.authenticated()要认证以后才能访问
.and().formLogin().loginPage("/admin/to/login/page.html").permitAll() //去往登录地址,任何人都可以访问
.loginProcessingUrl("/admin/security/do/login.html").permitAll() //指定登录表单的提交地址
.usernameParameter("loginAcct").passwordParameter("userPswd") //指定登录表单的name属性值
.defaultSuccessUrl("/admin/to/main/page.html") //登录验证成功后去的地址
.and().logout().logoutUrl("/admin/security/do/logout.html") //退出功能访问的地址
.logoutSuccessUrl("/index.html") //退出成功以后去的地址
//csrf一般都是开启的,这里是测试,为了方便先关闭
.and().csrf().disable()
;
}
}
6.2 自定义数据库认证类
定义好认证和权限的类,这里写具体通过数据库认证
@Service
public class CrowdFundingUserDetailService implements UserDetailsService {
@Autowired
private AdminMapper adminMapper;
@Autowired
private RoleMapper roleMapper;
@Autowired
private AuthMapper authMapper;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
//1 执行查询,根据用户名从数据库查询Admin对象
List<Admin> list = adminMapper.selectByUserName(username);
//2 检查list是否有效,这个是个工具类,不用在意,可以自己写,判断它是不是非空的就行
if(!CrowdFundingUtils.collectionEffective(list)) {
return null;
}
//3 list中取出admin对象
Admin admin=list.get(0);
//4 获取密码
//String userPwd=admin.getUserPswd(); 使用了自定义的User类后,因为直接传过去的是admin,就不用单独写了
//5 封装角色,权限信息
//创建存储角色、权限信息的集合
List<GrantedAuthority> authorities=new ArrayList<GrantedAuthority>();
// List<GrantedAuthority> authorities=AuthorityUtils.createAuthorityList("ROLE_admin1","ROLE_admin2","look1");
// 5.1 根据id查角色的信息
List<Role> roleList = roleMapper.selectAssignedRoleList(admin.getId());
for (Role role : roleList) {
//查询到的角色添加到集合中,添加角色要有ROLE_前缀
authorities.add(new SimpleGrantedAuthority("ROLE_"+role.getName()));
}
// 5.2 根据Id查权限的信息
List<Auth> authList=authMapper.selectAuthListByAdminId(admin.getId());
for (Auth auth : authList) {
// 得判断name是不是有效的,如果是空的要报错
if(!CrowdFundingUtils.stringEffective(auth.getName())) {
continue ;
}
//查询到的权限添加到集合中
authorities.add(new SimpleGrantedAuthority(auth.getName()));
}
//6 返回User对象,这个是原始的user对象,Security提供的,只有username和userPwd属性
// return new User(username,userPwd,authorities);
//6 返回User对象,这个SecurityAdmin是自己写的,把自己的实体类也写进去了,到时候就可以拿到其它属性了,
return new SecurityAdmin(admin, authorities);
}
}
6.3 自定义admin类
public class SecurityAdmin extends User {
private static final long serialVersionUID = 1L;
private Admin originalAdmin;
public SecurityAdmin(Admin admin, Collection<? extends GrantedAuthority> authorities) {
//调用父类的构造器
super(admin.getUserName(), admin.getUserPswd(), authorities);
this.originalAdmin=admin;
}
public Admin getOriginalAdmin() {
return originalAdmin;
}
}
7 security标签
7.1 引入jstl
<%@ taglib uri="http://www.springframework.org/security/tags" prefix="security" %>
7.2 security标签的使用
//显示用户名
<security:authentication property="name" />
<security:authentication property="principal.username" />
//取值,这里写了自定义的类,因为自定义的类的属性有限,想要取到更多值,自定义了类,类名叫originalAdmin,然后点它的属性就可以拿到,如果没有写自定义的类,那就是直接principal.userpwd或者principal.username
<security:authentication property="principal.originalAdmin.email"/>
//认证后显示的信息,也就是登录才能看到 isAuthenticated()
<security:authorize access="isAuthenticated()">
已经认证
</security:authorize>
//没有认证显示的信息
<security:authorize access="!isAuthenticated()">
已经认证
</security:authorize>
//拥有职员这个角色的用户才能看到这里的内容
<security:authorize access="hasRole('职员')">
拥有职员这个角色的用户才能看到这里的内容
</security:authorize>
//拥有user:get这项权限的用户才能看到这里的内容
<security:authorize access="hasAuthority('user:get')">
拥有user:get这项权限的用户才能看到这里的内容
</security:authorize>
8、BCryptPasswordEncoder加密
它每次加密的结果都不一样,但是只要加密前是同一个值,随便和哪次的加密结果对比都能验证成功。
public static void main(String[] args) {
BCryptPasswordEncoder encoder=new BCryptPasswordEncoder();
//加密
String password="";
for (int i = 0; i < 5; i++) {
password=encoder.encode("123");
System.out.println(password);
}
//通过encoder.matches验证是否一致
boolean isOK=encoder.matches("123", "$2a$10$VfEjWLbMFtyIKTBGYpppvuECgl5lgIA1dkIRcIgjMU1fzFUi2LwyG");
System.out.println(isOK);
}
8.1 加密自定义认证类的密码
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true) // 表示启用全局方法权限管理功能。
public class CrowdfundingSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UserDetailsService userDetailService;
// Spring在真正调用这个方法前会检查,IOC容器中是否已经有了对应的bean,
// 如果有,则不会真正调用这个方法。而是直接把IOC容器中的bean返回。
@Bean
public BCryptPasswordEncoder getPasswordEncoder() {
return new BCryptPasswordEncoder();
}
//认证规则
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
/* auth.inMemoryAuthentication() //在内存中,不经过数据库
.withUser("zhangsan").password("123") //定义用户名和密码
.roles("admin1"); //定义一个用户角色 */
//加密以后和数据库中的密文对比
auth.userDetailsService(userDetailService).passwordEncoder(getPasswordEncoder());
}
//授权规则
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/index.html", "/bootstrap/**",
"/css/**", "/fonts/**", "/img/**",
"/jquery/**", "/layer/**", "/script/**",
"/ztree/**","/testpage.html").permitAll() //设置权限,让这些地址没有登录就可以访问,permitAll():任何人都可以访问
.anyRequest().authenticated() //anyRequest()其它地址,.authenticated()要认证以后才能访问
.and().formLogin().loginPage("/admin/to/login/page.html").permitAll() //去往登录地址,任何人都可以访问
.loginProcessingUrl("/admin/security/do/login.html").permitAll() //指定登录表单的提交地址
.usernameParameter("loginAcct").passwordParameter("userPswd") //指定登录表单的name属性值
.defaultSuccessUrl("/admin/to/main/page.html") //登录验证成功后去的地址
.and().logout().logoutUrl("/admin/security/do/logout.html") //退出功能访问的地址
.logoutSuccessUrl("/index.html") //退出成功以后去的地址
.and().csrf().disable()
;
}
}
注解实现权限控制
@PreAuthorize(“hasAuthority(‘role:get’)”)
//SpEL表达式
//拥有role:get这个权限才能访问这个方法
@PreAuthorize(“hasAuthority(‘role:get’)”
//SpEL表达式
//拥有role:get这个权限才能访问这个方法
@PreAuthorize("hasAuthority('role:get')")
//根据条件查询角色
@RequestMapping("/role/search/by/keyword")
public ... ....(
}
工具类
基于SecurityContextHolder.getContext().getAuthentication()
封装的工具类,获取当前登陆用户的各种信息和一些校验的方法
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.util.PatternMatchUtils;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.constant.HttpStatus;
import com.ruoyi.common.core.domain.entity.SysRole;
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.exception.ServiceException;
/**
* 安全服务工具类
*/
public class SecurityUtils
{
/**
* 用户ID
**/
public static Long getUserId()
{
try
{
return getLoginUser().getUserId();
}
catch (Exception e)
{
throw new ServiceException("获取用户ID异常", HttpStatus.UNAUTHORIZED);
}
}
/**
* 获取部门ID
**/
public static Long getDeptId()
{
try
{
return getLoginUser().getDeptId();
}
catch (Exception e)
{
throw new ServiceException("获取部门ID异常", HttpStatus.UNAUTHORIZED);
}
}
/**
* 获取用户账户
**/
public static String getUsername()
{
try
{
return getLoginUser().getUsername();
}
catch (Exception e)
{
throw new ServiceException("获取用户账户异常", HttpStatus.UNAUTHORIZED);
}
}
/**
* 获取用户
**/
public static LoginUser getLoginUser()
{
try
{
return (LoginUser) getAuthentication().getPrincipal();
}
catch (Exception e)
{
throw new ServiceException("获取用户信息异常", HttpStatus.UNAUTHORIZED);
}
}
/**
* 获取Authentication
*/
public static Authentication getAuthentication()
{
return SecurityContextHolder.getContext().getAuthentication();
}
/**
* 生成BCryptPasswordEncoder密码
*
* @param password 密码
* @return 加密字符串
*/
public static String encryptPassword(String password)
{
BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
return passwordEncoder.encode(password);
}
/**
* 判断密码是否相同
*
* @param rawPassword 真实密码
* @param encodedPassword 加密后字符
* @return 结果
*/
public static boolean matchesPassword(String rawPassword, String encodedPassword)
{
BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
return passwordEncoder.matches(rawPassword, encodedPassword);
}
/**
* 是否为管理员
*
* @param userId 用户ID
* @return 结果
*/
public static boolean isAdmin(Long userId)
{
return userId != null && 1L == userId;
}
/**
* 验证用户是否具备某权限
*
* @param permission 权限字符串
* @return 用户是否具备某权限
*/
public static boolean hasPermi(String permission)
{
return hasPermi(getLoginUser().getPermissions(), permission);
}
/**
* 判断是否包含权限
*
* @param authorities 权限列表
* @param permission 权限字符串
* @return 用户是否具备某权限
*/
public static boolean hasPermi(Collection<String> authorities, String permission)
{
return authorities.stream().filter(StringUtils::hasText)
.anyMatch(x -> Constants.ALL_PERMISSION.equals(x) || PatternMatchUtils.simpleMatch(x, permission));
}
/**
* 验证用户是否拥有某个角色
*
* @param role 角色标识
* @return 用户是否具备某角色
*/
public static boolean hasRole(String role)
{
List<SysRole> roleList = getLoginUser().getUser().getRoles();
Collection<String> roles = roleList.stream().map(SysRole::getRoleKey).collect(Collectors.toSet());
return hasRole(roles, role);
}
/**
* 判断是否包含角色
*
* @param roles 角色列表
* @param role 角色
* @return 用户是否具备某角色权限
*/
public static boolean hasRole(Collection<String> roles, String role)
{
return roles.stream().filter(StringUtils::hasText)
.anyMatch(x -> Constants.SUPER_ADMIN.equals(x) || PatternMatchUtils.simpleMatch(x, role));
}
}