SpringSecurity


1、加入SpringSecurity依赖

<!-- SpringSecurityWeb应用进行权限管理 -->
		<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));
    }

}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值