前言

  • 我在毕设时为了方便设备与用户使用同一套后端而做的设置。
  • Springboot在分离前后端时可以直接使用JSON传输。
  • 如果有做权限需求,其实这个最重要的就是Spring Security JSON化问题,其他问题差不多就只是增删查改。
  • 这个security JSON化在网页上一搜就很多,但是可以直接拿来使用是真的没有。SO我就直接把我的代码贴出来啦。

代码

前端

html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<form class="layui-form changePwd">
<div style="margin:0 0 15px 110px;color:#f00;">使用说明:用户名与密码不得低于6位数!</div>
<div class="layui-form-item">
<label class="layui-form-label">用户名</label>
<div class="layui-input-block">
<input type="text" value="" placeholder="请输入用户名" lay-verify="required|userName" id="userName" class="layui-input ">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">密码</label>
<div class="layui-input-block">
<input type="password" value="" placeholder="请输入密码" lay-verify="required|newPwd" id="newPwd" class="layui-input pwd">
</div>
</div>
<div class="layui-form-item">
<div class="layui-input-block">
<button class="layui-btn" lay-submit="" lay-filter="login">立即登陆</button>
<button type="reset" class="layui-btn layui-btn-primary">重置</button>
</div>
</div>
</form>

JS

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
form.on("submit(login)",function(data){
var index = layer.msg('提交中,请稍候',{icon: 16,time:false,shade:0.8});
$.ajax({
url : "login",
type : "post",
// data表示发送的数据
data : JSON.stringify({
"${_csrf.parameterName}":"${_csrf.token}",
userName : $("#userName").val(),
password : $("#newPwd").val()
}),
// 定义发送请求的数据格式为JSON字符串
contentType : "application/json;charset=UTF-8",
// 定义回调响应的数据格式为JSON字符串,该属性可以省略
dataType : "json",
// 成功响应的结果
success : function(data) {
layer.msg(data.information);
if (data.status == "200") {
window.location.href = 'index.html';
return true;
}
return false;
},
error : function(data) {
layer.msg(data.information);
}
});
return false; //阻止表单跳转。如果需要表单跳转,去掉这段即可。
})

后端

security配置类——AppAuthenticationSuccessHandler

  • 看名字就知道是什么类了
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    import java.io.IOException;
    import java.util.ArrayList;
    import java.util.Collection;
    import java.util.List;

    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;

    import org.springframework.security.core.Authentication;
    import org.springframework.security.core.GrantedAuthority;
    import org.springframework.security.web.DefaultRedirectStrategy;
    import org.springframework.security.web.RedirectStrategy;
    import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler;
    import org.springframework.stereotype.Component;

    @Component
    public class AppAuthenticationSuccessHandler extends SimpleUrlAuthenticationSuccessHandler{
    // Spring Security 通过RedirectStrategy对象负责所有重定向事务
    private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();

    /*
    * 重写handle方法,方法中通过RedirectStrategy对象重定向到指定的url
    * */
    @Override
    protected void handle(HttpServletRequest request, HttpServletResponse response,
    Authentication authentication)
    throws IOException {
    // 通过determineTargetUrl方法返回需要跳转的url
    String targetUrl = determineTargetUrl(authentication);
    // 重定向请求到指定的url
    redirectStrategy.sendRedirect(request, response, targetUrl);
    }

    /*
    * 从Authentication对象中提取角色提取当前登录用户的角色,并根据其角色返回适当的URL。
    */
    protected String determineTargetUrl(Authentication authentication) {
    String url = "";

    // 获取当前登录用户的角色权限集合
    Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();

    List<String> roles = new ArrayList<String>();

    // 将角色名称添加到List集合
    for (GrantedAuthority a : authorities) {
    roles.add(a.getAuthority());
    }

    // 判断不同角色跳转到不同的url
    if (isAdmin(roles)) {
    url = "/page/admin/index.html";
    } else if (isUser(roles)) {
    url = "/index.html";
    } else {
    url = "/page/404.html";
    }
    System.out.println("url = " + url);
    return url;
    }

    private boolean isUser(List<String> roles) {
    if (roles.contains("ROLE_USER")) {
    return true;
    }
    return false;
    }

    private boolean isAdmin(List<String> roles) {
    if (roles.contains("ROLE_ADMIN")) {
    return true;
    }
    return false;
    }

    public void setRedirectStrategy(RedirectStrategy redirectStrategy) {
    this.redirectStrategy = redirectStrategy;
    }

    protected RedirectStrategy getRedirectStrategy() {
    return redirectStrategy;
    }
    }

    security配置类——AppSecurityConfigurer

  • Security核心配置类
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    import java.io.IOException;
    import java.io.PrintWriter;

    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;

    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.security.authentication.AuthenticationProvider;
    import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
    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.core.Authentication;
    import org.springframework.security.core.AuthenticationException;
    import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
    import org.springframework.security.crypto.password.PasswordEncoder;
    import org.springframework.security.web.authentication.AuthenticationFailureHandler;
    import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
    import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

    import com.fasterxml.jackson.databind.ObjectMapper;

    import ink.mastermind.AllINOne.pojo.Json;
    import ink.mastermind.AllINOne.service.UserService;

    /**
    * 自定义Spring Security认证处理类的时候
    * 我们需要继承自WebSecurityConfigurerAdapter来完成,相关配置重写对应 方法即可。
    * */
    @Configuration
    public class AppSecurityConfigurer extends WebSecurityConfigurerAdapter{

    // 依赖注入用户服务类
    @Autowired
    private UserService userService;

    // 依赖注入加密接口
    @Autowired
    private PasswordEncoder passwordEncoder;

    // 依赖注入用户认证接口
    @Autowired
    private AuthenticationProvider authenticationProvider;

    // 依赖注入认证处理成功类,验证用户成功后处理不同用户跳转到不同的页面
    @Autowired
    AppAuthenticationSuccessHandler appAuthenticationSuccessHandler;

    /*
    * BCryptPasswordEncoder是Spring Security提供的PasswordEncoder接口是实现类
    * 用来创建密码的加密程序,避免明文存储密码到数据库
    */
    @Bean
    public PasswordEncoder passwordEncoder() {
    return new BCryptPasswordEncoder();
    }

    // DaoAuthenticationProvider是Spring Security提供AuthenticationProvider的实现
    @Bean
    public AuthenticationProvider authenticationProvider() {
    System.out.println("AuthenticationProvider authenticationProvider");
    // 创建DaoAuthenticationProvider对象
    DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
    // 不要隐藏"用户未找到"的异常
    provider.setHideUserNotFoundExceptions(false);
    // 通过重写configure方法添加自定义的认证方式。
    provider.setUserDetailsService(userService);
    // 设置密码加密程序认证
    provider.setPasswordEncoder(passwordEncoder);

    //new BCryptPasswordEncoder().encode(fkUser.getPassword().trim()))
    return provider;
    }
    @Bean
    CustomAuthenticationFilter customAuthenticationFilter() throws Exception {
    CustomAuthenticationFilter filter = new CustomAuthenticationFilter();
    filter.setAuthenticationSuccessHandler(new AuthenticationSuccessHandler() {
    @Override
    public void onAuthenticationSuccess(HttpServletRequest req, HttpServletResponse resp, Authentication authentication) throws IOException, ServletException {
    resp.setContentType("application/json;charset=utf-8");
    PrintWriter out = resp.getWriter();
    Json json = new Json(200, "登录成功", null);
    out.write(new ObjectMapper().writeValueAsString(json));
    out.flush();
    out.close();
    }
    });
    filter.setAuthenticationFailureHandler(new AuthenticationFailureHandler() {
    @Override
    public void onAuthenticationFailure(HttpServletRequest req, HttpServletResponse resp, AuthenticationException e) throws IOException, ServletException {
    resp.setContentType("application/json;charset=utf-8");
    PrintWriter out = resp.getWriter();
    Json json = new Json(500, "登录失败", null);
    out.write(new ObjectMapper().writeValueAsString(json));
    out.flush();
    out.close();
    }
    });
    filter.setAuthenticationManager(authenticationManagerBean());
    return filter;
    }


    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    System.out.println("AppSecurityConfigurer configure auth......");
    // 设置认证方式。
    auth.authenticationProvider(authenticationProvider);
    }

    /**
    * 设置了登录页面,而且登录页面任何人都可以访问,然后设置了登录失败地址,也设置了注销请求,注销请求也是任何人都可以访问的。
    * permitAll表示该请求任何人都可以访问,.anyRequest().authenticated(),表示其他的请求都必须要有权限认证。
    * */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
    System.out.println("AppSecurityConfigurer configure http......");
    http.csrf().disable();
    http.headers().frameOptions().sameOrigin();
    http.authorizeRequests()
    // spring-security 5.0 之后需要过滤静态资源
    .antMatchers("/register.html","/register",
    "/login.html","/login",
    "/findPassword.html","/findPassword",
    "/json/**","/layui/**","/images/**","/css/**","/js/**").permitAll()
    .antMatchers("/page/**").hasAnyRole("USER","ADMIN")
    .antMatchers("/admin/**").hasAnyRole("ADMIN")
    .anyRequest().authenticated()
    .and()
    .formLogin().loginPage("/login").successHandler(appAuthenticationSuccessHandler)
    .usernameParameter("userName").passwordParameter("password")
    .and()
    .logout().permitAll()
    .and()
    .exceptionHandling().accessDeniedPage("/login.html");
    http.addFilterAt(customAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);

    }
    }

    security配置类——CustomAuthenticationFilter

  • 这个是一个读取JSON的过滤器。它是继承UsernamePasswordAuthenticationFilter的。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    import java.io.IOException;
    import java.io.InputStream;
    import java.util.Map;

    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;

    import org.springframework.http.MediaType;
    import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
    import org.springframework.security.core.Authentication;
    import org.springframework.security.core.AuthenticationException;
    import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

    import com.fasterxml.jackson.databind.ObjectMapper;

    public class CustomAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
    throws AuthenticationException {
    if (request.getContentType().equals(MediaType.APPLICATION_JSON_UTF8_VALUE)
    || request.getContentType().equals(MediaType.APPLICATION_JSON_VALUE)) {
    ObjectMapper mapper = new ObjectMapper();
    UsernamePasswordAuthenticationToken authRequest = null;
    try (InputStream is = request.getInputStream()) {
    Map<String, String> authenticationBean = mapper.readValue(is, Map.class);
    authRequest = new UsernamePasswordAuthenticationToken(authenticationBean.get("userName"),
    authenticationBean.get("password"));
    } catch (IOException e) {
    e.printStackTrace();
    authRequest = new UsernamePasswordAuthenticationToken("", "");
    } finally {
    setDetails(request, authRequest);
    return this.getAuthenticationManager().authenticate(authRequest);
    }
    } else {
    return super.attemptAuthentication(request, response);
    }
    }
    }

    security配置类——MyPasswordEncoder

  • 密码编码解码器
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    import org.springframework.security.crypto.bcrypt.BCrypt;
    import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
    import org.springframework.security.crypto.password.PasswordEncoder;

    public class MyPasswordEncoder implements PasswordEncoder{
    public static MyPasswordEncoder m = new MyPasswordEncoder();

    @Override
    public String encode(CharSequence arg0) {
    return new BCryptPasswordEncoder().encode(arg0.toString());
    }

    @Override
    public boolean matches(CharSequence raw, String encode) {
    return BCrypt.checkpw(raw.toString(), encode);
    }

    }

    用户服务类–UserService

  • 这个类是为了调用数据库中用户,密码与权限所使用的。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    import java.util.ArrayList;
    import java.util.List;

    import javax.annotation.Resource;

    import org.springframework.security.core.GrantedAuthority;
    import org.springframework.security.core.authority.SimpleGrantedAuthority;
    import org.springframework.security.core.userdetails.UserDetails;
    import org.springframework.security.core.userdetails.UserDetailsService;
    import org.springframework.security.core.userdetails.UsernameNotFoundException;
    import org.springframework.stereotype.Service;
    import org.springframework.transaction.annotation.Transactional;

    import ink.mastermind.AllINOne.dao.UserDao;
    import ink.mastermind.AllINOne.pojo.User;
    import ink.mastermind.AllINOne.security.MyPasswordEncoder;

    @Service("userService")
    public class UserService implements UserDetailsService {

    @Resource
    private UserDao userDao;

    /**
    * @param user 注册用户
    */
    @Transactional
    public void register(User user) {
    // TODO Auto-generated method stub
    user.setPassword(MyPasswordEncoder.m.encode(user.getPassword()));
    this.userDao.save(user);
    }

    /**
    * @param user
    * @return 通过用户名查找用户并返回
    */
    @Transactional
    public List<User> findUserByUserName(User user) {
    // TODO Auto-generated method stub
    return this.userDao.findByUserName(user.getUserName());
    }

    /**
    * security模块
    */
    public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException {
    // 调用持久层接口方法查找用户
    List<User> l = this.userDao.findByUserName(userName);
    User user = (l != null && !l.isEmpty()) ? l.get(0) : null;
    if (user == null) {
    throw new UsernameNotFoundException("用户名不存在");
    }
    // 创建List集合,用来保存用户权限,GrantedAuthority对象代表赋予给当前用户的权限
    List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
    authorities.add(new SimpleGrantedAuthority(user.getRole()));
    return new org.springframework.security.core.userdetails.User(user.getUserName(), user.getPassword().trim(),
    authorities);
    }

    }

    JSON自制封装模块

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    public class Json {
    private Integer status;
    private String information;
    private Object object;

    public Json() {
    super();
    // TODO Auto-generated constructor stub
    }

    public Integer getStatus() {
    return status;
    }

    public void setStatus(Integer status) {
    this.status = status;
    }

    public String getInformation() {
    return information;
    }

    public void setInformation(String information) {
    this.information = information;
    }

    public Object getObject() {
    return object;
    }

    public void setObject(Object object) {
    this.object = object;
    }

    @Override
    public String toString() {
    return "Json [status=" + status + ", information=" + information + ", object=" + object + "]";
    }

    public Json(Integer status, String information, Object object) {
    super();
    this.status = status;
    this.information = information;
    this.object = object;
    }

    }

    链接

  • 原来链接