springsecurity
新建springboot项目
1.导入依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.20</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.0.5</version>
</dependency>
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf-spring5</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-java8time</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
</dependencies>
2.application.properties
spring.datasource.url=jdbc:mysql://localhost:3306/textnode?serverTimeZone=UTC&useUnicode=true&characterEncoding=UTF-8
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
3. entity层
//和数据库字段一样
@Data
@TableName("tian")
public class UserLogin {
@TableId(type = IdType.AUTO)
private int id;
private String name;
private String password;
}
4.dao层
@Mapper
@Repository
public interface UserMapper extends BaseMapper<UserLogin> {
}
5.service层
public interface UserService extends IService<UserLogin> {
}
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, UserLogin> implements UserService {
}
6.controller层
package com.example.springsecuritydemo.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@Controller
public class LoginController {
// @RequestMapping("/login")
// public String login(){
// return "redirect:main.html";
// }
@RequestMapping("/toMain")
public String toMain(){
return "redirect:main.html";
}
@RequestMapping("/toError")
public String toError(){
return "redirect:error.html";
}
}
config
package com.example.springsecuritydemo.config;
import com.example.springsecuritydemo.handle.MyAuthenticationFailHandle;
import com.example.springsecuritydemo.handle.MyAuthenticationSuccessHandle;
import com.example.springsecuritydemo.service.UserDetailServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
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.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UserDetailServiceImpl userDetailService;
/*
// 没有加此段代码 数据库密码如果是明文,UserDetailServiceImpl传入的明文密码登录不进去,加了就可以或者 UserDetailServiceImpl传入的密码是经过加密返回User对象就可以登录成功
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailService).passwordEncoder(getPw()); //注释掉原本从内存中加载用户的代码。 }
}
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
http.formLogin()
// .usernameParameter("username123")//自定义入参和前端的name属性一样
// .passwordParameter("password123")//自定义入参
.loginPage("/login.html") //自定义登录页面
.loginProcessingUrl("/login")//必须和表单提交的接口一样,回去执行自定义登录逻辑
// .successForwardUrl("/toMain")//登录成功后跳转的页面 POST请求
//前后端分离 跳转不归我们管 编写handle.MyAuthenticationSuccessHandle
.successHandler(new MyAuthenticationSuccessHandle("http://www.baidu.com"))
// .failureForwardUrl("/toError") //登录失败后跳转的页面 POST请求
.failureHandler(new MyAuthenticationFailHandle("/error.html"))//自定义登陆失败处理器
;
//授权
http.authorizeRequests()
//放行 不用登录
.antMatchers("/login.html").permitAll()
//放行 不用登录
.antMatchers("/error.html").permitAll()
//拦截所有请求
.anyRequest().authenticated();
//防止网站攻击
http.csrf().disable();//登录失败可能存在的原因
}
@Bean
public PasswordEncoder getPw() {
return new BCryptPasswordEncoder();
}
}
UserDetailServiceImpl
package com.example.springsecuritydemo.service;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.example.springsecuritydemo.entity.UserLogin;
import com.example.springsecuritydemo.mapper.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.*;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
@Service
public class UserDetailServiceImpl implements UserDetailsService{
// @Autowired
// UserService service;
@Autowired
UserMapper mapper;
@Autowired
PasswordEncoder passwordEncoder;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
QueryWrapper<UserLogin> wrapper = new QueryWrapper<>();
wrapper.eq("name",username);
UserLogin one = mapper.selectOne(wrapper);
if(one==null){
throw new UsernameNotFoundException("用户名不存在");
}
// String password = passwordEncoder.encode(one.getPassword()); //加密后传入
// System.out.println(password);
return new User(username,one.getPassword(), AuthorityUtils.commaSeparatedStringToAuthorityList("admin,normal"));
}
}
handle/MyAuthenticationFailHandle
package com.example.springsecuritydemo.handle;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class MyAuthenticationFailHandle implements AuthenticationFailureHandler {
private String url;
public MyAuthenticationFailHandle(String url) {
this.url = url;
}
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
response.sendRedirect(url);
}
}
handle/MyAuthenticationSuccessHandle
package com.example.springsecuritydemo.handle;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class MyAuthenticationSuccessHandle implements AuthenticationSuccessHandler {
private String url;
public MyAuthenticationSuccessHandle(String url) {
this.url = url;
}
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
response.sendRedirect(url);
}
}
前端static/login.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="/login" method="post">
<!-- 注意name属性必须是 username 可以后端改-->
用户名 : <input type="text" name="username">
<!-- 注意name属性必须是 password -->
密码 : <input type="password" name="password">
<input type="submit" value="登录">
</form>
</body>
</html>
error.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
登陆失败
<a href="/login.html">跳到登陆页面</a>
</body>
</html>
main.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
登录成功
</body>
</html>
授权
anyRequest()
注意要放在最后面 是按顺序的 ,放最前面其他放行的请求都会被拦截
//授权
http.authorizeRequests()
//放行 不用登录
.antMatchers("/login.html").permitAll()
//放行 不用登录
.antMatchers("/error.html").permitAll()
//拦截所有请求
.anyRequest().authenticated();
antMatchers()
放行 静态资源
? 匹配一个字符
* 匹配0个或多个字符
** 匹配0个或多个目录
//例
.antMatchers("/css/**","/js/**","/images/**").permitAll()
.antMatchers("/**/*.png").permitAll()
regexMatchers()
//正则匹配
.regexMatchers(".+[.]png").permitAll()
//还有一个参数控制请求方法的 必须post才放行
.regexMatchers(HttpMethod.POST,"/demo").permitAll()
mvcMatchers()
//整个项目加前缀访问路径
//只在mvcMatchers里面有servletPath方法
.mvcMatchers("/demo").servletPath("/xxxx").permitAll()
等价于
.antMatchers("/xxxx/demo").permitAll()
控制的permitAll()还有其他的控制方法

基于权限判断
//严格区分大小写
.antMatchers("/main1.html").hasAuthority("admin").permitAll()
//只要有一个权限就可以访问
.antMatchers("/main1.html").hasAnyAuthority("admin","admiN").permitAll()
基于角色判断
//给用户加角色 必须是 ROLE_ 开头
return new User(username,one.getPassword(), AuthorityUtils.commaSeparatedStringToAuthorityList("admin,normal,ROLE_abc"));
//严格区分大小写
.antMatchers("/main1.html").hasRole("abc").permitAll()
//只要有一个角色就可以访问
.antMatchers("/main1.html").hasAnyRole("abc","ABC").permitAll()
基于ip判断
.antMatchers("/main1.html").hasIpAddress("127.0.0.1").permitAll()
自定义403处理方案
定义配置类

使用

基于Access的访问控制
- 前面的控制方法都是基于Access
//写法
.antMatchers("/main1.html").access("permitAll")
.antMatchers("/main1.html").access("hasRole('admin')")
自定义Access方法
编写接口及其实现类

使用

启动访问main.html 报错 没有 跳转的路径 的权限
添加访问权限

启动访问 成功~
用户授权的注解使用
@Secured
表示用户具有某个角色,可以访问方法
1.启动类开启注解

2.使用
- 区分大小写

@PreAuthorize
在进入方法之前做验证,看是否有权限进入
区分大小写 可以 加前缀ROLE_

@PostAuthorize
方法执行之后校验 没什么大用
RememberMe
1.导入依赖
基于springjdbc的 但springjdbc很少用 用mybatis+mysql
2.编写mysql连接配置
3.使用 配置类增加配置



4.前端加上记住我

thymeleaf中spring security的使用
1.导入依赖

2.前端页面

3.现象

设置用户角色和权限

退出登录
直接在前端写上退出链接
<a href="/logout">退出</a>
但地址栏会有lougout,不想看见怎么办?
配置类配置
//退出
http.logout()
//自定义退出 前端的退出请求也得是这个 最好就用默认的
//.logoutUrl("/user/logout")
.logoutUrl("/logout")
//退出成功后跳转的页面
.logoutSuccessUrl("/login.html")
CRSF
跨站请求伪造
跨域: 网络协议 ip地址 端口中任何一个不相同就是跨域请求

此时就登陆不成功
解决

Oauth2认证

第三方认证技术方案最主要是解决认证协议的通用标准问题,因为要实现跨系统认证,各系统之间要遵循一定的接口协议。

环境搭建
1.新建springboot工程
2.导入依赖 注意springboot版本
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.11.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.tian</groupId>
<artifactId>springsecurityoauth2</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springsecurityoauth2</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Greenwich.SR2</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.2</version>
<configuration>
<skipTests>true</skipTests>
</configuration>
</plugin>
</plugins>
</build>
</project>
3.pojo/user
package com.tian.springsecurityoauth2.pojo;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.Collection;
import java.util.List;
public class User implements UserDetails {
private String username;
private String password;
private List<GrantedAuthority> authorities;
public User(String username, String password, List<GrantedAuthority> authorities) {
this.username = username;
this.password = password;
this.authorities = authorities;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return authorities;
}
@Override
public String getPassword() {
return password;
}
@Override
public String getUsername() {
return username;
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
}
4.service/UserService
package com.tian.springsecurityoauth2.service;
import com.tian.springsecurityoauth2.pojo.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
@Service
public class UserService implements UserDetailsService {
@Autowired
private PasswordEncoder passwordEncoder;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
String password = passwordEncoder.encode("123456");
return new User(username,password,AuthorityUtils.commaSeparatedStringToAuthorityList("admin"));
}
}
5.controller/UserController
@RestController
@RequestMapping("/user")
public class UserController {
@RequestMapping("/getCurrent")
public Object getCurrent(Authentication authentication){
return authentication.getPrincipal();
}
}
6.config/SecurityConfig
package com.tian.springsecurityoauth2.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/oauth/**","/login/**",
"/logout/**")
.permitAll()
.anyRequest()
.authenticated()
.and()
.formLogin()
.permitAll()
.and()
.csrf().disable();
}
@Bean
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
}
7.config/AuthorzationServerConfig
package com.tian.springsecurityoauth2.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
@Configuration
@EnableAuthorizationServer
public class AuthorzationServerConfig extends AuthorizationServerConfigurerAdapter {
@Autowired
private PasswordEncoder passwordEncoder;
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
//客户端ID
.withClient("client")
//密钥
.secret(passwordEncoder.encode("112233"))
//重定向地址
.redirectUris("http://www.baidu.com")
//授权范围
.scopes("all")
//授权类型 - authorization_code 授权码模式
.authorizedGrantTypes("authorization_code");
}
}
8.config/ResourceServerConfig
package com.tian.springsecurityoauth2.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
@Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.anyRequest()
.authenticated()
.and().requestMatchers()
.antMatchers("/user/**")
;
}
}
启动访问
http://localhost:8080/oauth/authorize?response_type=code&client_id=client&redirect_uri=http://www.baidu.com&scope=all
返回到springsecurity自定义的登录页面
输入 admin 123456 跳到这个页面 点击允许 跳转到百度

注意百度地址栏有个授权码

拿着返回的token请求资源

实际上token不会存在内存,我们这边用redis存储
redis存储token
1.添加依赖

2.配置文件

3.配置类

4.使用


JWT
是一个标准
JJWT
是JWT的实现
1.创建springboot项目
2.导入依赖

3.启动类
4.测试

5.现象

解析token

现象

JWT过期校验

自定义claims

获取

9054

被折叠的 条评论
为什么被折叠?



