在用spring + mybatis 做项目。之前安全验证用basic 方式现在想换成jwt。
jwt :
JWS模式对这个内容进行了数字化签名。这个内容被用来存放JWT的声明.服务端签名出JWT并且发送到客户端,并在用户成功认证后进行应答。服务器期望客户端在下次请求的时候将JWS作为请求的一部分,发送回服务端。
如果我们处理的客户端是欺骗者则么办?这就是签名(signature)需要出场的地方了。签名携带了完整的可验证的信息。换句话说,服务器可以确认,接收到的JWT声明里的JWS是没有经过欺骗客户端、中间者进行修改的。
服务端通过验证消息的签名来确保客户端没有修改声明。如果服务端检测到任何修改,可以采取适当的动作(拒绝这次请求或者锁定客户端之类的)
客户端同样可以验证签名,为了做到这点,客户端也需要服务端的secret(密钥)(如果这个JWT签名是HMAC算法),或者需要服务端对公钥(如果这个WJT是数字化签名)
特别注意:对于JWS,荷载(声明部分)没有进行加密,所以,不要发送任何敏感信息.
首先在web.xml 中
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/spring-mybatis.xml
/WEB-INF/spring-security.xml
classpath:applicationContext.xml
</param-value>
</context-param>
<!-- Security -->
<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>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet>
<servlet-name>fly.fky</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>fly.fky</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>
spring-sucurity.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/security"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-4.2.xsd">
<context:annotation-config />
<global-method-security pre-post-annotations="enabled" />
<beans:bean id="passwordEncoder" class="org.springframework.security.crypto.password.StandardPasswordEncoder">
<beans:constructor-arg value="ThisIsASecretSoChangeMe" />
</beans:bean>
<!--
<authentication-manager id="authenticationManager"
erase-credentials="false">
<authentication-provider user-service-ref="userService">
<password-encoder ref="passwordEncoder"></password-encoder>
</authentication-provider>
</authentication-manager>
<http pattern="/**" entry-point-ref="unauthorizedEntryPoint"
create-session="stateless">
<csrf disabled="true" />
<custom-filter before="FORM_LOGIN_FILTER" ref="jwtAuthenticationFilter" />
</http>
<beans:bean id="jwtAuthenticationFilter"
class="fly.fky.restapi.security.JwtAuthenticationFilter">
<beans:property name="authenticationManager" ref="authenticationManager" />
<beans:property name="authenticationSuccessHandler"
ref="jwtAuthenticationSuccessHandler" />
</beans:bean>
<beans:bean id="unauthorizedEntryPoint"
class="fly.fky.restapi.service.UnauthorizedEntryPoint" />
<beans:bean id="jwtAuthenticationSuccessHandler"
class="fly.fky.restapi.security.JwtAuthenticationSuccessHandler" />
<!-- authentication manager -->
<authentication-manager id="authenticationManager">
<authentication-provider ref="jwtAuthenticationProvider">
</authentication-provider>
</authentication-manager>
<beans:bean id="jwtAuthenticationProvider"
class="fly.fky.restapi.security.JwtAuthenticationProvider" />
</beans:beans>
jwt filter 文件
public class JwtAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
public JwtAuthenticationFilter() {
super("/**");
}
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
throws AuthenticationException, IOException, ServletException {
String header = request.getHeader("Authorization");
if (header == null || !header.startsWith("Bearer ")) {
throw new RestAPIAuthenticationException(MessageToClientFormat.formateMsg("No JWT token found in request headers"));
}
String authToken = header.substring(7);
return JwtTokenUtil.getAuthentication(authToken);
}
}
JwtAuthenticationProvider
public class JwtAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider {
@Resource
private UserService userService;
@Override
protected void additionalAuthenticationChecks(UserDetails userDetails,
UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
if (authentication.getCredentials() == null) {
logger.debug("Authentication failed: no credentials provided");
throw new BadCredentialsException(messages.getMessage(
"AbstractUserDetailsAuthenticationProvider.badCredentials",
"Bad credentials"));
}
String presentedPassword = authentication.getCredentials().toString();
if (!userService.isPasswordMatched(presentedPassword, userDetails.getPassword())) {
logger.debug("Authentication failed: password does not match stored value");
throw new BadCredentialsException(messages.getMessage(
"AbstractUserDetailsAuthenticationProvider.badCredentials",
"Bad credentials"));
}
}
@Override
protected UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication)
throws AuthenticationException {
return userService.loadUserByUsername(username);
}
}