spring Security用户认证框架
spring security 简介
spring security 的核心功能主要包括:
认证 (你是谁)
授权 (你能干什么)
攻击防护 (防止伪造身份)
其核心就是一组过滤器链,项目启动后将会自动配置。最核心的就是 Basic Authentication Filter 用来认证用户的身份,一个在spring security中一种过滤器处理一种认证方式。
比如,对于username password认证过滤器来说,
会检查是否是一个登录请求;
是否包含username 和 password (也就是该过滤器需要的一些认证信息) ;
如果不满足则放行给下一个。
1.创建mavenWeb项目
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.sxw</groupId> <artifactId>spring-security</artifactId> <version>1.0-SNAPSHOT</version> <packaging>war</packaging> <url>http://www.example.com</url> <properties> <jdk.version>1.8</jdk.version> <spring.version>4.3.10.RELEASE</spring.version> <spring.security.version>4.2.3.RELEASE</spring.security.version> <jstl.version>1.2</jstl.version> <servlet.version>2.5</servlet.version> </properties> <dependencies> <!-- Spring dependencies --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>${spring.version}</version> </dependency> <!-- 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> <!-- jstl for jsp page --> <dependency> <groupId>jstl</groupId> <artifactId>jstl</artifactId> <version>${jstl.version}</version> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>servlet-api</artifactId> <version>${servlet.version}</version> <scope>provided</scope> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.9.5</version> </dependency> </dependencies> <build> <plugins> <!-- jdk版本插件 --> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.2</version> <configuration> <source>1.8</source> <target>1.8</target> <encoding>UTF-8</encoding> <showWarnings>true</showWarnings> </configuration> </plugin> <!-- tomcat7插件 --> <plugin> <groupId>org.apache.tomcat.maven</groupId> <artifactId>tomcat7-maven-plugin</artifactId> <version>2.1</version> <configuration> <port>8080</port> <path>/ss1</path> <server>tomcat7</server> </configuration> </plugin> </plugins> </build> </project>
2.创建Spring相关配置文件
1)applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <import resource="classpath:spring-security.xml"/> </beans>
2)springmvc.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:contenxt="http://www.springframework.org/schema/context" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!-- 扫描Controller类--> <contenxt:component-scan base-package="com.sxw"/> <!--注解方式处理器映射器和处理器适配器 --> <mvc:annotation-driven></mvc:annotation-driven> <!--视图解析器--> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <!--前缀 --> <property name="prefix" value="/WEB-INF/jsp/"/> <!-- 后缀--> <property name="suffix" value=".jsp"/> </bean> </beans>
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:contenxt="http://www.springframework.org/schema/context" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!-- 扫描Controller类--> <contenxt:component-scan base-package="com.sxw"/> <!--注解方式处理器映射器和处理器适配器 --> <mvc:annotation-driven></mvc:annotation-driven> <!--视图解析器--> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <!--前缀 --> <property name="prefix" value="/WEB-INF/jsp/"/> <!-- 后缀--> <property name="suffix" value=".jsp"/> </bean> </beans>
3)spring-security.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:security="http://www.springframework.org/schema/security" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.2.xsd http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-4.2.xsd"> <!-- <security:http>: spring过滤器链配置: 1)需要拦截什么资源 2)什么资源什么角色权限 3)定义认证方式:HttpBasic,FormLogin(*) 4)定义登录页面,定义登录请求地址,定义错误处理方式 --> <security:http> <!-- pattern: 需要拦截资源 access: 拦截方式 isFullyAuthenticated(): 该资源需要认证才可以访问 isAnonymous() 只有匿名用户才可以访问(如果登录用户就无法访问) permitAll() :允许所有人( 匿名和登录用户)方法 --> <security:intercept-url pattern="/product/index" access="permitAll()"/> <security:intercept-url pattern="/userLogin" access="permitAll()"/> <security:intercept-url pattern="/product/add" access="hasRole('ROLE_USER')"/> <security:intercept-url pattern="/product/update" access="hasRole('ROLE_USER')"/> <security:intercept-url pattern="/product/list" access="hasRole('ROLE_ADMIN')"/> <security:intercept-url pattern="/product/delete" access="hasRole('ROLE_ADMIN')"/> <security:intercept-url pattern="/**" access="isFullyAuthenticated()"/> <!-- security:http-basic: 使用HttpBasic方式进行登录(认证) --> <!--<security:http-basic/>--> <!--表单形式的验证 login-page 自定义登录页面 login-processing-url:登录请求的地址 authentication-success-handler-ref:登录成功之后的情况 authentication-failure-handler-ref: 登录失败之后的情况--> <security:form-login login-page="/userLogin" login-processing-url="/securityLogin" default-target-url="/product/index" authentication-success-handler-ref="myAuthenticationSuccessHandler" authentication-failure-handler-ref="myAuthenticationFailureHandler" /> <!--关闭spring security CSRF机制--> <security:csrf disabled="true"/> <!-- 自定义权限不足处理 --> <security:access-denied-handler error-page="/error"/> </security:http> <security:authentication-manager> <!-- security:authentication-manager: 认证管理器 1)认证信息提供方式(账户名,密码,当前用户权限) --> <!-- <security:authentication-provider> <security:user-service> <security:user name="eric" password="123456" authorities="ROLE_USER"/> <security:user name="jack" password="123456" authorities="ROLE_ADMIN"/> </security:user-service> </security:authentication-provider>--> <!--定义USerDetailServer方式--> <security:authentication-provider user-service-ref="myUserDemoServer"/> </security:authentication-manager> <bean id="myUserDemoServer" class="com.sxw.security.MyUserDemoServer"/> <bean id="myAuthenticationSuccessHandler" class="com.sxw.security.MyAuthenticationSuccessHandler"/> <bean id="myAuthenticationFailureHandler" class="com.sxw.security.MyAuthenticationFailureHandler"/> </beans>
3.编写web.xml
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0"> <!-- 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> <!-- 启动Spring --> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <context-param> <param-name>contextConfigLocation</param-name> <param-value> classpath:applicationContext.xml </param-value> </context-param> <!--启动SpringMVC--> <servlet> <servlet-name>DispatcherServlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:springmvc.xml</param-value> </init-param> <!-- 服务器启动加载Servlet--> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>DispatcherServlet</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> </web-app>
3.编写拦截器
1)登录成功处理
/** * @description: 登录成功处理 * @author: * @createDate: 2020/4/4 * @version: 1.0 */ public class MyAuthenticationSuccessHandler implements AuthenticationSuccessHandler { /*json框架的工具类*/ private ObjectMapper objectMapper = new ObjectMapper(); /** * * @description:TODO * @params:3. 认证成功后的信息 * @return: * @author: * @time: 2020/4/4 12:26 */ @Override public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException { //返回json字符串 Map<String,Object> result = new HashMap(); result.put("success",true); String json =objectMapper.writeValueAsString(result); response.setContentType("text/json;charset=utf-8"); response.getWriter().write(json); } }
2)登录失败的逻辑
/** * @description: 登录失败的逻辑 * @author: * @createDate: 2020/4/4 * @version: 1.0 */ public class MyAuthenticationFailureHandler implements AuthenticationFailureHandler { /*json框架的工具类*/ private ObjectMapper objectMapper = new ObjectMapper(); @Override public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) throws IOException, ServletException { //返回json字符串 Map<String,Object> result = new HashMap(); result.put("Failure",false); String json =objectMapper.writeValueAsString(result); response.setContentType("text/json;charset=utf-8"); response.getWriter().write(json); } }
3)用户权限的管理
/** * @description: 用户权限的管理 * @author: * @createDate: 2020/4/4 * @version: 1.0 */ public class MyUserDemoServer implements UserDetailsService { /** * * @description:读取数据库的信息 * @params: * @return: * @author: * @time: 2020/4/4 12:00 */ @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { //UserDetails: 封装用户数据的接口 User user = new User("eric","123456", AuthorityUtils. commaSeparatedStringToAuthorityList("ROLE_USER")); return user; } }
2.SpringBoot整合
1)添加依赖
<?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>1.5.4.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.sxw</groupId> <artifactId>springsecurityboot</artifactId> <version>0.0.1-SNAPSHOT</version> <name>springsecurityboot</name> <packaging>war</packaging> <description>Demo project for Spring Boot</description> <dependencies> <!-- web支持,SpringMVC, Servlet支持等 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- thymeleaf --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>1.3.0</version> </dependency> <!-- Spring Security --> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-web</artifactId> <version>4.2.3.RELEASE</version> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-config</artifactId> <version>4.2.3.RELEASE</version> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-taglibs</artifactId> <version>4.2.3.RELEASE</version> </dependency> <!-- jstl for jsp page --> <dependency> <groupId>jstl</groupId> <artifactId>jstl</artifactId> <version>${jstl.version}</version> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>servlet-api</artifactId> <version>2.5</version> <scope>provided</scope> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.7</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.41</version> </dependency> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-test</artifactId> <version>2.2.6.RELEASE</version> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>5.2.5.RELEASE</version> <scope>test</scope> </dependency> </dependencies> <properties> <java.version>1.8</java.version> <!-- 修改thymeleaf的版本 --> <thymeleaf.version>3.0.2.RELEASE</thymeleaf.version> <thymeleaf-layout-dialect.version>2.0.4</thymeleaf-layout-dialect.version> </properties> </project>
@Component public class User implements UserDetails{ private Integer id; //int(10) NOT NULL, private String username; //varchar(50) DEFAULT NULL, private String realname; //varchar(50) DEFAULT NULL, private String password; //varchar(50) DEFAULT NULL, private Date createDate; //date DEFAULT NULL, private Date lastLoginTime; //date DEFAULT NULL, private boolean enabled; //int(5) DEFAULT NULL, private boolean accountNonExpired; //int(5) DEFAULT NULL, private boolean accountNonLocked; //int(5) DEFAULT NULL, private boolean credentialsNonExpired; //int(5) DEFAULT NULL, //用户拥有的所有权限 private List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>(); public List<GrantedAuthority> getAuthorities() { return authorities; } public void setAuthorities(List<GrantedAuthority> authorities) { this.authorities = authorities; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } @Override public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getRealname() { return realname; } public void setRealname(String realname) { this.realname = realname; } @Override public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public Date getCreateDate() { return createDate; } public void setCreateDate(Date createDate) { this.createDate = createDate; } public Date getLastLoginTime() { return lastLoginTime; } public void setLastLoginTime(Date lastLoginTime) { this.lastLoginTime = lastLoginTime; } @Override public boolean isEnabled() { return enabled; } public void setEnabled(boolean enabled) { this.enabled = enabled; } @Override public boolean isAccountNonExpired() { return accountNonExpired; } public void setAccountNonExpired(boolean accountNonExpired) { this.accountNonExpired = accountNonExpired; } @Override public boolean isAccountNonLocked() { return accountNonLocked; } public void setAccountNonLocked(boolean accountNonLocked) { this.accountNonLocked = accountNonLocked; } @Override public boolean isCredentialsNonExpired() { return credentialsNonExpired; } public void setCredentialsNonExpired(boolean credentialsNonExpired) { this.credentialsNonExpired = credentialsNonExpired; } @Override public String toString() { return "User{" + "id=" + id + ", username='" + username + '\'' + ", realname='" + realname + '\'' + ", password='" + password + '\'' + ", createDate=" + createDate + ", lastLoginTime=" + lastLoginTime + ", enabled=" + enabled + ", accountNonExpired=" + accountNonExpired + ", accountNonLocked=" + accountNonLocked + ", credentialsNonExpired=" + credentialsNonExpired + ", authorities=" + authorities + '}'; } }
public void testUpdatePassword(){ User user = new User(); user.setUsername("jack"); //哈希算法加密 PasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); user.setPassword(passwordEncoder.encode("123456")); userMapper.updatePassword(user); }
SpringSecurity的配置文件
@Configuration @EnableWebSecurity //启动SpringSecurity过滤器链 public class SpringSecurityConfig extends WebSecurityConfigurerAdapter { //该方法的作用就是代替之前配置:<security:authentication-manager> @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.inMemoryAuthentication().withUser("eric").password("123456").authorities("PRODUCT_ADD"); } //该方法的作用就是代替之前配置:<security:http> @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .antMatchers("/product/add").hasAuthority("PRODUCT_ADD") .antMatchers("/product/update").hasAuthority("PRODUCT_UPDATE") .antMatchers("/product/list").hasAuthority("PRODUCT_LIST") .antMatchers("/product/delete").hasAuthority("PRODUCT_DELETE") .antMatchers("/login").permitAll() .antMatchers("/**") .fullyAuthenticated() .and() .formLogin().loginPage("/login") .and() .csrf().disable(); } }
登录错误处理
@Configuration public class ErrorPageConfig { @Bean public EmbeddedServletContainerCustomizer embeddedServletContainerCustomizer(){ return new EmbeddedServletContainerCustomizer(){ //ErrorPage:定义错误页面 //参数一:HttpStatus.FORBIDDEN: 该错误接收什么错误状态码 //参数二:交给哪个请求处理 @Override public void customize(ConfigurableEmbeddedServletContainer container) { container.addErrorPages(new ErrorPage(HttpStatus.FORBIDDEN,"/403")); } }; } }
@WebFilter(urlPatterns = "/view/*") public class LoginFilter implements Filter { @Autowired private RedisTemplate redisTemplate; @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { /*校验登录登录用户状态*/ HttpServletRequest request = (HttpServletRequest) servletRequest; HttpServletResponse response = (HttpServletResponse) servletResponse; String origin = request.getHeader("Origin"); response.setHeader("Access-Control-Allow-Origin",origin); response.setHeader("Access-Control-Allow-Methods","POST,GET,OPTIONS,DELETE"); response.setHeader("Access-Control-Max-Age","3600"); response.setHeader("Access-Control-Allow-Headers","x-Request-with,Authorization,token"); /*获取Header中的参数*/ String token = request.getHeader("token"); token = token== null ? "" :token; Long expire = redisTemplate.getExpire(token); if (expire>0){//登录状态 /*重置登录时间*/ redisTemplate.expire(token, 30L, TimeUnit.MINUTES); filterChain.doFilter(servletRequest,servletResponse); }else { String string = JsonUntil.toJson(ResultVOUntils.error("未登录",1)); } } @Override public void destroy() { } }